From 0a61cb99f8dc373242ae52825801ce2097e42d0c Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 10 Dec 2020 10:48:42 +0100 Subject: [PATCH 001/790] [core] Fixed a bug repeating a conclusion HS with rejection (#1650) --- srtcore/api.cpp | 7 +- srtcore/api.h | 4 +- srtcore/core.cpp | 350 +++++++++++++++++++++++++++++------------------ srtcore/core.h | 12 +- 4 files changed, 235 insertions(+), 138 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 2a23dace2..2e9c4014b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -490,9 +490,10 @@ SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) } int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, - CHandShake& w_hs, int& w_error) + CHandShake& w_hs, int& w_error, CUDT*& w_acpu) { CUDTSocket* ns = NULL; + w_acpu = NULL; w_error = SRT_REJ_IPE; @@ -534,6 +535,10 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; + // Report the original UDT because it will be + // required to complete the HS data for conclusion response. + w_acpu = ns->m_pUDT; + return 0; //except for this situation a new connection should be started diff --git a/srtcore/api.h b/srtcore/api.h index bce15c9c1..8ad6bb3fd 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -229,10 +229,12 @@ friend class CRendezvousQueue; /// @param [in] listen the listening UDT socket; /// @param [in] peer peer address. /// @param [in,out] hs handshake information from peer side (in), negotiated value (out); + /// @param [out] w_error error code when failed + /// @param [out] w_acpu entity of accepted socket, if connection already exists /// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error. int newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, - CHandShake& w_hs, int& w_error); + CHandShake& w_hs, int& w_error, CUDT*& w_acpu); int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b6d1fb0d6..afc5ec32e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1588,7 +1588,7 @@ void CUDT::setListenState() m_bListening = true; } -size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int hs_version) +size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) { if (srtlen < SRT_HS_E_SIZE) { @@ -1599,24 +1599,24 @@ size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int srtlen = SRT_HS_E_SIZE; // We use only that much space. - memset((srtdata), 0, sizeof(uint32_t) * srtlen); + memset((aw_srtdata), 0, sizeof(uint32_t) * srtlen); /* Current version (1.x.x) SRT handshake */ - srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ - srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); + aw_srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ + aw_srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); switch (msgtype) { case SRT_CMD_HSREQ: - return fillSrtHandshake_HSREQ(srtdata, srtlen, hs_version); + return fillSrtHandshake_HSREQ((aw_srtdata), srtlen, hs_version); case SRT_CMD_HSRSP: - return fillSrtHandshake_HSRSP(srtdata, srtlen, hs_version); + return fillSrtHandshake_HSRSP((aw_srtdata), srtlen, hs_version); default: LOGC(cnlog.Fatal, log << "IPE: fillSrtHandshake/sendSrtMsg called with value " << msgtype); return 0; } } -size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // INITIATOR sends HSREQ. @@ -1636,50 +1636,50 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused * Sent data is real-time, use Time-based Packet Delivery, * set option bit and configured delay */ - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; if (hs_version < CUDT::HS_VERSION_SRT1) { // HSv4 - this uses only one value. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms); } else { // HSv5 - this will be understood only since this version when this exists. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); // And in the reverse direction. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; - srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); // This wasn't there for HSv4, this setting is only for the receiver. // HSv5 is bidirectional, so every party is a receiver. if (m_bTLPktDrop) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; } } if (m_bRcvNakReport) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; // I support SRT_OPT_REXMITFLG. Do you? - srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; // Declare the API used. The flag is set for "stream" API because // the older versions will never set this flag, but all old SRT versions use message API. if (!m_bMessageAPI) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; HLOGC(cnlog.Debug, - log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) - << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" - << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); return 3; } -size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // Setting m_tsRcvPeerStartTime is done in processSrtMsg_HSREQ(), so // this condition will be skipped only if this function is called without @@ -1698,18 +1698,18 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused * We got and transposed peer start time (HandShake request timestamp), * we can support Timestamp-based Packet Delivery */ - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; if (hs_version < HS_VERSION_SRT1) { // HSv4 - this uses only one value - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms); } else { // HSv5 - this puts "agent's" latency into RCV field and "peer's" - // into SND field. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); } } else @@ -1723,8 +1723,8 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused { // HSv5 is bidirectional - so send the TSBPDSND flag, and place also the // peer's latency into SND field. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; - srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); HLOGC(cnlog.Debug, log << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms); @@ -1737,14 +1737,14 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused } if (m_bTLPktDrop) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; if (m_bRcvNakReport) { // HSv5: Note that this setting is independent on the value of // m_bPeerNakReport, which represent this setting in the peer. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; /* * NAK Report is so efficient at controlling bandwidth that sender TLPktDrop * is not needed. SRT 1.0.5 to 1.0.7 sender TLPktDrop combined with SRT 1.0 @@ -1754,7 +1754,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused * from enabling Too-Late Packet Drop. */ if (m_lPeerSrtVersion <= SrtVersion(1, 0, 7)) - srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; } if (m_lSrtVersion >= SrtVersion(1, 2, 0)) @@ -1768,7 +1768,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused else { // Request that the rexmit bit be used as a part of msgno. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; HLOGF(cnlog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too."); } } @@ -1780,9 +1780,9 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused } HLOGC(cnlog.Debug, - log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) - << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" - << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); return 3; } @@ -2229,7 +2229,7 @@ bool CUDT::createSrtHandshake( // ra_size after that // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements. // WILL BE CHANGED HERE. - ra_size = fillSrtHandshake(p + offset, total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); + ra_size = fillSrtHandshake((p + offset), total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); HLOGC(cnlog.Debug, @@ -4389,6 +4389,85 @@ void CUDT::cookieContest() m_SrtHsSide = HSD_DRAW; } +// This function should complete the data for KMX needed for an out-of-band +// handshake response. Possibilities are: +// - There's no KMX (including first responder's handshake in rendezvous). This writes 0 to w_kmdatasize. +// - The encryption status is failure. Respond with fail code and w_kmdatasize = 1. +// - The last KMX was successful. Respond with the original kmdata and their size in w_kmdatasize. +EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) +{ + // If the last CONCLUSION message didn't contain the KMX extension, there's + // no key recorded yet, so it can't be extracted. Mark this w_kmdatasize empty though. + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ)) + { + // This is a periodic handshake update, so you need to extract the KM data from the + // first message, provided that it is there. + size_t msgsize = m_pCryptoControl->getKmMsg_size(0); + if (msgsize == 0) + { + switch (m_pCryptoControl->m_RcvKmState) + { + // If the KMX process ended up with a failure, the KMX is not recorded. + // In this case as the KMRSP answer the "failure status" should be crafted. + case SRT_KM_S_NOSECRET: + case SRT_KM_S_BADSECRET: + { + HLOGC(cnlog.Debug, + log << "craftKmResponse: No KMX recorded, status = " + << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it."); + + // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, + // that is, copy the NOSECRET code into KMX message. + memcpy((aw_kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); + w_kmdatasize = 1; + } + break; // Treat as ACCEPT in general; might change to REJECT on enforced-encryption + + default: + // Remaining values: + // UNSECURED: should not fall here at all + // SECURING: should not happen in HSv5 + // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) + { + m_RejectReason = SRT_REJ_IPE; + // Remaining situations: + // - password only on this site: shouldn't be considered to be sent to a no-password site + LOGC(cnlog.Error, + log << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" + << KmStateStr(m_pCryptoControl->m_RcvKmState) + << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); + return CONN_REJECT; + } + break; + } + } + else + { + w_kmdatasize = msgsize / 4; + if (msgsize > w_kmdatasize * 4) + { + // Sanity check + LOGC(cnlog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); + memset((aw_kmdata + (w_kmdatasize * 4)), 0, msgsize - (w_kmdatasize * 4)); + ++w_kmdatasize; + } + + HLOGC(cnlog.Debug, + log << "craftKmResponse: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + << w_kmdatasize); + memcpy((aw_kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); + } + } + else + { + HLOGC(cnlog.Debug, log << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP"); + w_kmdatasize = 0; + } + + return CONN_ACCEPT; +} + EConnectStatus CUDT::processRendezvous( const CPacket& response, const sockaddr_any& serv_addr, EReadStatus rst, CPacket& w_reqpkt) @@ -4485,73 +4564,11 @@ EConnectStatus CUDT::processRendezvous( } else { - // If the last CONCLUSION message didn't contain the KMX extension, there's - // no key recorded yet, so it can't be extracted. Mark this kmdatasize empty though. - int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); - if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ)) - { - // This is a periodic handshake update, so you need to extract the KM data from the - // first message, provided that it is there. - size_t msgsize = m_pCryptoControl->getKmMsg_size(0); - if (msgsize == 0) - { - switch (m_pCryptoControl->m_RcvKmState) - { - // If the KMX process ended up with a failure, the KMX is not recorded. - // In this case as the KMRSP answer the "failure status" should be crafted. - case SRT_KM_S_NOSECRET: - case SRT_KM_S_BADSECRET: - { - HLOGC(cnlog.Debug, - log << "processRendezvous: No KMX recorded, status = NOSECRET. Respond with NOSECRET."); - - // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, - // that is, copy the NOSECRET code into KMX message. - memcpy((kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); - kmdatasize = 1; - } - break; - - default: - // Remaining values: - // UNSECURED: should not fall here at alll - // SECURING: should not happen in HSv5 - // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) - { - m_RejectReason = SRT_REJ_IPE; - // Remaining situations: - // - password only on this site: shouldn't be considered to be sent to a no-password site - LOGC(cnlog.Error, - log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" - << KmStateStr(m_pCryptoControl->m_RcvKmState) - << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); - return CONN_REJECT; - } - break; - } - } - else - { - kmdatasize = msgsize / 4; - if (msgsize > kmdatasize * 4) - { - // Sanity check - LOGC(cnlog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); - memset((kmdata + (kmdatasize * 4)), 0, msgsize - (kmdatasize * 4)); - ++kmdatasize; - } - - HLOGC(cnlog.Debug, - log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" - << kmdatasize); - memcpy((kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); - } - } - else - { - HLOGC(cnlog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); - kmdatasize = 0; - } + // This is a repeated handshake, so you can't use the incoming data to + // prepare data for createSrtHandshake. They have to be extracted from inside. + EConnectStatus conn = craftKmResponse((kmdata), (kmdatasize)); + if (conn != CONN_ACCEPT) + return conn; } // No matter the value of needs_extension, the extension is always needed @@ -5863,6 +5880,25 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD return true; } +void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) +{ + // this is a reponse handshake + w_hs.m_iReqType = URQ_CONCLUSION; + w_hs.m_iMSS = m_iMSS; + w_hs.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; + w_hs.m_iID = m_SocketID; + + if (w_hs.m_iVersion > HS_VERSION_UDT4) + { + // The version is agreed; this code is executed only in case + // when AGENT is listener. In this case, conclusion response + // must always contain HSv5 handshake extensions. + w_hs.m_extension = true; + } + + CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); +} + void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << "acceptAndRespond: setting up data according to handshake"); @@ -5872,45 +5908,28 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly at SRT HS // Uses the smaller MSS between the peers - if (w_hs.m_iMSS > m_iMSS) - w_hs.m_iMSS = m_iMSS; - else - m_iMSS = w_hs.m_iMSS; + m_iMSS = std::min(m_iMSS, w_hs.m_iMSS); // exchange info for maximum flow window size - m_iFlowWindowSize = w_hs.m_iFlightFlagSize; - w_hs.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; - + m_iFlowWindowSize = w_hs.m_iFlightFlagSize; m_iPeerISN = w_hs.m_iISN; - - setInitialRcvSeq(m_iPeerISN); - m_iRcvCurrPhySeqNo = w_hs.m_iISN - 1; + setInitialRcvSeq(m_iPeerISN); + m_iRcvCurrPhySeqNo = CSeqNo::decseq(w_hs.m_iISN); m_PeerID = w_hs.m_iID; - w_hs.m_iID = m_SocketID; // use peer's ISN and send it back for security check m_iISN = w_hs.m_iISN; - setInitialSndSeq(m_iISN); + setInitialSndSeq(m_iISN); m_SndLastAck2Time = steady_clock::now(); - // this is a reponse handshake - w_hs.m_iReqType = URQ_CONCLUSION; - - if (w_hs.m_iVersion > HS_VERSION_UDT4) - { - // The version is agreed; this code is executed only in case - // when AGENT is listener. In this case, conclusion response - // must always contain HSv5 handshake extensions. - w_hs.m_extension = true; - } - // get local IP address and send the peer its IP address (because UDP cannot get local IP address) memcpy((m_piSelfIP), w_hs.m_piPeerIP, sizeof m_piSelfIP); m_parent->m_SelfAddr = agent; CIPAddress::pton((m_parent->m_SelfAddr), m_piSelfIP, peer); - CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); + + rewriteHandshakeData(peer, (w_hs)); int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -6003,6 +6022,9 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); + // Save the handshake in m_ConnRes in case when needs repeating. + m_ConnRes = w_hs; + // send the response to the peer, see listen() for more discussions about this // XXX Here create CONCLUSION RESPONSE with: // - just the UDT handshake, if HS_VERSION_UDT4, @@ -6023,9 +6045,6 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } - // Set target socket ID to the value from received handshake's source ID. - response.m_iID = m_PeerID; - #if ENABLE_HEAVY_LOGGING { // To make sure what REALLY is being sent, parse back the handshake @@ -6044,7 +6063,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - m_pSndQueue->sendto(peer, response); + addressAndSend((response)); } // This function is required to be called when a caller receives an INDUCTION @@ -8806,7 +8825,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) CHandShake req; req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); - HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); + HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION @@ -8841,7 +8860,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) << " WITH SRT ext"); - have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize); + have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); if (!have_hsreq) { initdata.m_iVersion = 0; @@ -8879,6 +8898,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) else { initdata.m_iVersion = HS_VERSION_UDT4; + kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX } initdata.m_extension = have_hsreq; @@ -8887,7 +8907,6 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - // XXX here interpret SRT handshake extension CPacket response; response.setControl(UMSG_HANDSHAKE); response.allocate(m_iMaxSRTPayloadSize); @@ -10813,7 +10832,8 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) else { int error = SRT_REJ_UNKNOWN; - int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error)); + CUDT* acpu = NULL; + int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); // This is listener - m_RejectReason need not be set // because listener has no functionality of giving the app @@ -10853,9 +10873,69 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // reused for the connection rejection response (see URQ_ERROR_REJECT set // as m_iReqType). + // The 'acpu' should be set to a new socket, if found; + // this means simultaneously that result == 0, but it's safest to + // check this condition only. This means that 'newConnection' found + // that the connection attempt has already been accepted, just the + // caller side somehow didn't get the answer. The rule is that every + // connection request HS must be completed with a symmetric HS response, + // so craft one here. + + // Note that this function runs in the listener socket context, while 'acpu' + // is the CUDT entity for the accepted socket. + if (acpu) + { + // This is an existing connection, so the handshake is only needed + // because of the rule that every handshake request must be covered + // by the handshake response. It wouldn't be good to call interpretSrtHandshake + // here because the data from the handshake have been already interpreted + // and recorded. We just need to craft a response. + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: sending REPEATED handshake response req=" + << RequestTypeStr(hs.m_iReqType)); + + // Rewrite already updated previously data in acceptAndRespond + acpu->rewriteHandshakeData(acpu->m_PeerAddr, (hs)); + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + EConnectStatus conn = CONN_ACCEPT; + + if (hs.m_iVersion >= HS_VERSION_SRT1) + { + // Always attach extension. + hs.m_extension = true; + conn = acpu->craftKmResponse((kmdata), (kmdatasize)); + } + else + { + kmdatasize = 0; + } + + if (conn != CONN_ACCEPT) + return conn; + + packet.setLength(m_iMaxSRTPayloadSize); + if (!acpu->createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, + kmdata, kmdatasize, + (packet), (hs))) + { + HLOGC(cnlog.Debug, + log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); + result = -1; // enforce fallthrough for the below condition! + hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? SRT_REJ_IPE : m_RejectReason); + } + else + { + // Send the crafted handshake + HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); + acpu->addressAndSend((packet)); + } + } + // send back a response if connection failed or connection already existed - // new connection response should be sent in acceptAndRespond() - if (result != 1) + // (or the above procedure failed) + if (result == -1) { HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" @@ -10868,6 +10948,8 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); } + // new connection response should be sent in acceptAndRespond() + // turn the socket writable if this is the first time when this was found out. else { // a new connection has been created, enable epoll for write diff --git a/srtcore/core.h b/srtcore/core.h index 41b9a7569..3fb8d6a05 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -549,6 +549,7 @@ class CUDT void applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr); + SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield); @@ -592,11 +593,18 @@ class CUDT void checkNeedDrop(bool& bCongestion); - /// Connect to a UDT entity listening at address "peer", which has sent "hs" request. + /// Connect to a UDT entity as per hs request. This will update + /// required data in the entity, then update them also in the hs structure, + /// and then send the response back to the caller. + /// @param agent [in] The address to which the UDT entity is bound. /// @param peer [in] The address of the listening UDT entity. + /// @param hspkt [in] The original packet that brought the handshake. /// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out). - void acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& hs); + + /// Write back to the hs structure the data after they have been + /// negotiated by acceptAndRespond. + void rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs); bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt); /// Close the opened UDT entity. From 0add6cc392c43853ec79ecd70886b2406015d399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 10 Dec 2020 17:12:19 +0100 Subject: [PATCH 002/790] [core] Fixed formal problems detected by clang --- srtcore/common.h | 2 +- srtcore/core.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/srtcore/common.h b/srtcore/common.h index f5d042f09..edd44eb30 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -771,7 +771,7 @@ class RollNumber return right < *this; } - bool operator=(const this_t& right) const + bool operator==(const this_t& right) const { return number == right.number; } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index afc5ec32e..34f14efa1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9668,8 +9668,10 @@ int CUDT::processData(CUnit* in_unit) std::string s = tns1.str(); tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); - ThreadName tn(tns2.str().c_str()); - const char* thname = tns2.str().c_str(); + const string& tn = tns2.str(); + + ThreadName tnkeep(tn.c_str()); + const char* thname = tn.c_str(); #else const char* thname = "SRT:TsbPd"; #endif From 86327e92f22fb25be3ab9d0ee73e5736ff241254 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 11 Dec 2020 17:07:37 +0100 Subject: [PATCH 003/790] [tests] Check IPv6 connection and socket address (#1670) --- .appveyor.yml | 2 +- .github/workflows/cxx11-macos.yaml | 4 +- .github/workflows/cxx11-ubuntu.yaml | 4 +- .github/workflows/cxx11-win.yaml | 6 +- .travis.yml | 2 +- CMakeLists.txt | 17 ++- test/filelist.maf | 1 + test/test_enforced_encryption.cpp | 4 +- test/test_epoll.cpp | 4 +- test/test_ipv6.cpp | 187 ++++++++++++++++++++++++++++ 10 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 test/test_ipv6.cpp diff --git a/.appveyor.yml b/.appveyor.yml index e496d6344..ab90ec09c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -22,7 +22,7 @@ build_script: - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Debug") { Exit-AppveyorBuild } # just skip 2013 debug build for speed test_script: - - ps: if ( $Env:RUN_UNIT_TESTS ) { cd ./_build; ctest --extra-verbose -C $Env:CONFIGURATION; cd ../ } + - ps: if ( $Env:RUN_UNIT_TESTS ) { cd ./_build; ctest -E "TestIPv6.v6_calls_v4" --extra-verbose -C $Env:CONFIGURATION; cd ../ } after_build: - cmd: >- diff --git a/.github/workflows/cxx11-macos.yaml b/.github/workflows/cxx11-macos.yaml index 57cc5af79..c3918d8f6 100644 --- a/.github/workflows/cxx11-macos.yaml +++ b/.github/workflows/cxx11-macos.yaml @@ -1,4 +1,4 @@ -name: cxx11_win +name: cxx11 on: push: @@ -8,7 +8,7 @@ on: jobs: build: - name: macos-cxx11 + name: macos runs-on: macos-latest steps: diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 44dd06ce0..6f54be036 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -1,4 +1,4 @@ -name: cxx11_win +name: cxx11 on: push: @@ -8,7 +8,7 @@ on: jobs: build: - name: ubuntu-cxx11 + name: ubuntu runs-on: ubuntu-18.04 steps: diff --git a/.github/workflows/cxx11-win.yaml b/.github/workflows/cxx11-win.yaml index cf9dcd871..7b5d3b666 100644 --- a/.github/workflows/cxx11-win.yaml +++ b/.github/workflows/cxx11-win.yaml @@ -1,4 +1,4 @@ -name: cxx11_win +name: cxx11 on: push: @@ -9,7 +9,7 @@ on: jobs: build: - name: windows-cxx11 + name: windows runs-on: windows-latest steps: @@ -21,4 +21,4 @@ jobs: - name: build run: cd _build && cmake --build ./ --config Release - name: test - run: cd _build && ctest --extra-verbose -C Release + run: cd _build && ctest -E "TestIPv6.v6_calls_v4" --extra-verbose -C Release diff --git a/.travis.yml b/.travis.yml index 4d27056a6..04c9319ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,7 +77,7 @@ script: make -j$(nproc); fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then - ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6"; + ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; fi - source ./scripts/collect-gcov.sh - if (( "$RUN_CODECOV" )); then diff --git a/CMakeLists.txt b/CMakeLists.txt index 2270ce380..baf1bf541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1210,12 +1210,19 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) ${PTHREAD_LIBRARY} ) - add_test( - NAME + if (${CMAKE_VERSION} VERSION_LESS "3.10.0") + add_test( + NAME test-srt + COMMAND ${CMAKE_BINARY_DIR}/test-srt + ) + else() + cmake_policy(SET CMP0057 NEW) # Support the new IN_LIST operator. + gtest_add_tests( test-srt - COMMAND - ${CMAKE_BINARY_DIR}/test-srt - ) + "" + AUTO + ) + endif() enable_testing() diff --git a/test/filelist.maf b/test/filelist.maf index 431373a46..77f0d4b1f 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -10,6 +10,7 @@ test_enforced_encryption.cpp test_epoll.cpp test_fec_rebuilding.cpp test_file_transmission.cpp +test_ipv6.cpp test_list.cpp test_listen_callback.cpp test_muxer.cpp diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index eeddfb922..c50afb416 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -626,7 +626,7 @@ static const char* const socket_state_array[] = { const char* const* TestEnforcedEncryption::m_socket_state = socket_state_array+1; /** - * @fn TEST_F(TestEnforcedEncryption, PasswordLength) + * @fn TestEnforcedEncryption.PasswordLength * @brief The password length should belong to the interval of [10; 80] */ TEST_F(TestEnforcedEncryption, PasswordLength) @@ -661,7 +661,7 @@ TEST_F(TestEnforcedEncryption, PasswordLength) /** - * @fn TEST_F(TestEnforcedEncryption, SetGetDefault) + * @fn TestEnforcedEncryption.SetGetDefault * @brief The default value for the enforced encryption should be ON */ TEST_F(TestEnforcedEncryption, SetGetDefault) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index ebeb0d73a..8445450d3 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -348,9 +348,9 @@ TEST(CEPoll, NotifyConnectionBreak) // The caller will close connection after 1 second auto close_res = std::async(std::launch::async, [&client_sock]() { - cout << "TEST(async call): WILL CLOSE client connection in 3s\n"; + cout << "(async call): WILL CLOSE client connection in 3s\n"; this_thread::sleep_for(chrono::seconds(1)); - cout << "TEST(async call): Closing client connection\n"; + cout << "(async call): Closing client connection\n"; return srt_close(client_sock); }); diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp new file mode 100644 index 000000000..2ca256f1d --- /dev/null +++ b/test/test_ipv6.cpp @@ -0,0 +1,187 @@ +#include "gtest/gtest.h" +#include +#include +#include "srt.h" +#include "netinet_any.h" + +inline std::string operator "" _S(const char* src, std::size_t) +{ + return std::string(src); +} + +class TestIPv6 + : public ::testing::Test +{ +protected: + int yes = 1; + int no = 0; + + TestIPv6() + { + // initialization code here + } + + ~TestIPv6() + { + // cleanup any pending stuff, but no exceptions allowed + } + +protected: + // SetUp() is run immediately before a test starts. + void SetUp() + { + ASSERT_GE(srt_startup(), 0); + + m_caller_sock = srt_create_socket(); + ASSERT_NE(m_caller_sock, SRT_ERROR); + + m_listener_sock = srt_create_socket(); + ASSERT_NE(m_listener_sock, SRT_ERROR); + } + + void TearDown() + { + // Code here will be called just after the test completes. + // OK to throw exceptions from here if needed. + srt_close(m_listener_sock); + srt_close(m_caller_sock); + srt_cleanup(); + } + +public: + void ClientThread(int family, const std::string& address) + { + sockaddr_any sa (family); + sa.hport(m_listen_port); + ASSERT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); + + std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + + ASSERT_NE(srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa), SRT_ERROR); + + PrintAddresses(m_caller_sock, "CALLER"); + } + + std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; + + void ShowAddress(std::string src, const sockaddr_any& w) + { + ASSERT_NE(fam.count(w.family()), 0) << "INVALID FAMILY"; + std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + } + + sockaddr_any DoAccept() + { + sockaddr_any sc1; + + SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); + EXPECT_NE(accepted_sock, SRT_INVALID_SOCK); + + PrintAddresses(accepted_sock, "ACCEPTED"); + + sockaddr_any sn; + EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); + + int32_t ipv6_zero [] = {0, 0, 0, 0}; + EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0) + << "EMPTY address in srt_getsockname"; + + srt_close(accepted_sock); + return sn; + } + +private: + void PrintAddresses(SRTSOCKET sock, const char* who) + { + sockaddr_any sa; + int sa_len = sa.storage_size(); + srt_getsockname(sock, sa.get(), &sa_len); + ShowAddress(std::string(who) + " Sock name: ", sa); + //std::cout << who << " Sock name: " << << sa.str() << std::endl; + + sa_len = sa.storage_size(); + srt_getpeername(sock, sa.get(), &sa_len); + //std::cout << who << " Peer name: " << << sa.str() << std::endl; + ShowAddress(std::string(who) + " Peer name: ", sa); + } + +protected: + SRTSOCKET m_caller_sock; + SRTSOCKET m_listener_sock; + const int m_listen_port = 4200; +}; + + +TEST_F(TestIPv6, v4_calls_v6_mapped) +{ + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &no, sizeof no), 0); + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1"_S); + + const sockaddr_any sa_accepted = DoAccept(); + EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200"_S); + + client.join(); +} + +TEST_F(TestIPv6, v6_calls_v6_mapped) +{ + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &no, sizeof no), 0); + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + + const sockaddr_any sa_accepted = DoAccept(); + EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + + client.join(); +} + +TEST_F(TestIPv6, v6_calls_v6) +{ + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv6. + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0); + ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + + const sockaddr_any sa_accepted = DoAccept(); + EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + + client.join(); +} + +TEST_F(TestIPv6, v6_calls_v4) +{ + sockaddr_any sa (AF_INET); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv4. + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"_S); + + const sockaddr_any sa_accepted = DoAccept(); + EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200"_S); + + client.join(); +} + From f9644153130424d2c0cf153b853dfc8db1492ea4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 7 Dec 2020 16:08:45 +0100 Subject: [PATCH 004/790] [core] Minor compiler warning fixes (conversion with possible loss of data) --- srtcore/buffer.cpp | 2 +- srtcore/channel.cpp | 6 +++--- srtcore/common.cpp | 2 +- srtcore/common.h | 2 +- srtcore/core.cpp | 14 +++++++------- srtcore/core.h | 2 +- srtcore/crypto.cpp | 4 ++-- srtcore/packet.h | 2 +- srtcore/utilities.h | 8 +++----- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 5fa566ae9..a3654eb92 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -933,7 +933,7 @@ int CRcvBuffer::readBuffer(char* data, int len) break; /* too early for this unit, return whatever was copied */ } - const int pktlen = pkt.getLength(); + const int pktlen = (int) pkt.getLength(); const int remain_pktlen = pktlen - m_iNotch; const int unitsize = std::min(remain_pktlen, rs); diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index b371e11ab..91aea4eb9 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -247,7 +247,7 @@ void CChannel::open(int family) ::freeaddrinfo(res); throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } - m_BindAddr = sockaddr_any(res->ai_addr, res->ai_addrlen); + m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t) res->ai_addrlen); ::freeaddrinfo(res); @@ -538,14 +538,14 @@ void CChannel::getSockAddr(sockaddr_any& w_addr) const // space to copy the socket name, it doesn't have to be correlated // with the address family. So the maximum space for any name, // regardless of the family, does the job. - socklen_t namelen = w_addr.storage_size(); + socklen_t namelen = (socklen_t) w_addr.storage_size(); ::getsockname(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } void CChannel::getPeerAddr(sockaddr_any& w_addr) const { - socklen_t namelen = w_addr.storage_size(); + socklen_t namelen = (socklen_t) w_addr.storage_size(); ::getpeername(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 52c19dbfe..1eff9d934 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -579,7 +579,7 @@ void CMD5::compute(const char* input, unsigned char result[16]) md5_state_t state; md5_init(&state); - md5_append(&state, (const md5_byte_t *)input, strlen(input)); + md5_append(&state, (const md5_byte_t *)input, (int) strlen(input)); md5_finish(&state, result); } diff --git a/srtcore/common.h b/srtcore/common.h index edd44eb30..99005d1be 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1416,7 +1416,7 @@ struct PacketMetric void update(size_t mult, uint64_t value) { - pkts += mult; + pkts += (uint32_t) mult; bytes += mult * value; } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 34f14efa1..692c109e1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1183,7 +1183,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_PBKEYLEN: if (m_pCryptoControl) - *(int32_t *)optval = m_pCryptoControl->KeyLen(); // Running Key length. + *(int32_t *)optval = (int32_t) m_pCryptoControl->KeyLen(); // Running Key length. else *(int32_t *)optval = m_iSndCryptoKeyLen; // May be 0. optlen = sizeof(int32_t); @@ -1255,14 +1255,14 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); strcpy((char *)optval, m_sStreamName.c_str()); - optlen = m_sStreamName.size(); + optlen = (int) m_sStreamName.size(); break; case SRTO_CONGESTION: { string tt = m_CongCtl.selected_name(); strcpy((char *)optval, tt.c_str()); - optlen = tt.size(); + optlen = (int) tt.size(); } break; @@ -1273,7 +1273,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_PAYLOADSIZE: optlen = sizeof(int); - *(int *)optval = m_zOPT_ExpPayloadSize; + *(int *)optval = (int) m_zOPT_ExpPayloadSize; break; #if ENABLE_EXPERIMENTAL_BONDING @@ -1308,7 +1308,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); strcpy((char *)optval, m_OPT_PktFilterConfigString.c_str()); - optlen = m_OPT_PktFilterConfigString.size(); + optlen = (int) m_OPT_PktFilterConfigString.size(); break; case SRTO_RETRANSMITALGO: @@ -1803,7 +1803,7 @@ size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) return srtlen; } -void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, int srtlen_in) +void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) { CPacket srtpkt; int32_t srtcmd = (int32_t)cmd; @@ -1819,7 +1819,7 @@ void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, int srtlen_in) // for incoming data. We have a guarantee that it won't be larger than SRTDATA_MAXSIZE. uint32_t srtdata[SRTDATA_SIZE]; - int srtlen = 0; + size_t srtlen = 0; if (cmd == SRT_CMD_REJECT) { diff --git a/srtcore/core.h b/srtcore/core.h index 3fb8d6a05..0b2dd5247 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -370,7 +370,7 @@ class CUDT static std::vector existingSockets(); void addressAndSend(CPacket& pkt); - void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, int srtlen_in = 0); + void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); bool isOPT_TsbPd() const { return m_bOPT_TsbPd; } int RTT() const { return m_iRTT; } diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index d412bfe17..82231c748 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -449,7 +449,7 @@ void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED) HLOGC(cnlog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen << " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry); m_SndKmLastTime = now; - m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t)); + m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t)); } } } @@ -522,7 +522,7 @@ void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional) { HLOGC(cnlog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen << " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry); - m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t)); + m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t)); sent++; } } diff --git a/srtcore/packet.h b/srtcore/packet.h index 31dbda411..1a43531b4 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -113,7 +113,7 @@ class IOVector inline void setLength(size_t length) { #ifdef _WIN32 - len = length; + len = (ULONG) length; #else iov_len = length; #endif diff --git a/srtcore/utilities.h b/srtcore/utilities.h index c81e5ba45..f0ad80271 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -899,14 +899,14 @@ struct MapProxy } }; +/// Print some hash-based stamp of the first 16 bytes in the buffer inline std::string BufferStamp(const char* mem, size_t size) { using namespace std; char spread[16]; - int n = 16-size; - if (n > 0) - memset((spread + 16 - n), 0, n); + if (size < 16) + memset((spread + size), 0, 16 - size); memcpy((spread), mem, min(size_t(16), size)); // Now prepare 4 cells for uint32_t. @@ -924,9 +924,7 @@ inline std::string BufferStamp(const char* mem, size_t size) } // Convert to hex string - ostringstream os; - os << hex << uppercase << setfill('0') << setw(8) << sum; return os.str(); From fd82e83da893a4f6a1f06e9e84de404832925543 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 11 Dec 2020 18:23:40 +0100 Subject: [PATCH 005/790] [apps] Show warning if UDP target is without host --- apps/transmitmedia.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 7ab167c9f..b024eb6d8 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -1012,6 +1012,9 @@ class UdpTarget: public Target, public UdpCommon public: UdpTarget(string host, int port, const map& attr ) { + if (host.empty()) + cerr << "\nWARN Host for UDP target is not provided. Will send to localhost:" << port << ".\n"; + Setup(host, port, attr); if (adapter != "") { From 6f953e3dcd4a912077decb1ea52e13b542b4a204 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 14 Dec 2020 17:39:35 +0100 Subject: [PATCH 006/790] [docs] Fixed wrong description concerning passphrase (#1701) --- docs/APISocketOptions.md | 23 +++++++++++++---------- srtcore/srt.h | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 82da159cf..5e7313526 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -867,20 +867,23 @@ For details, see [Packet Filtering & FEC](packet-filtering-and-fec.md). | `SRTO_PASSPHRASE` | 0.0.0 | pre | `string` | | "" |[10..79]| W | GSD | Sets the passphrase for encryption. This enables encryption on this party (or -disables it, if an empty passphrase is passed). +disables it, if an empty passphrase is passed). The password must be minimum +10 and maximum 79 characters long. The passphrase is the shared secret between the sender and the receiver. It is used to generate the Key Encrypting Key using [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) -(Password-Based Key Derivation Function 2). It is used on the receiver only if -the received data is encrypted. +(Password-Based Key Derivation Function 2). + +When a socket with configured passphrase is being connected, the peer must +have the same password set, or the connection is rejected. This behavior can be +changed by [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION). Note that since the introduction of bidirectional support, there's only one -initial SEK to encrypt the stream (new keys after refreshing will be updated -independently), and there's no distinction between "service party that defines -the password" and "client party that is required to set matching password" - both -parties are equivalent, and in order to have a working encrypted connection, they -have to simply set the same passphrase. Otherwise the connection is rejected by -default (see also [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION)). +initial encryption key to encrypt the stream (new keys after refreshing will be +updated independently), and there's no distinction between "service party that +defines the password" and "client party that is required to set matching +password" - both parties are equivalent, and in order to have a working +encrypted connection, they have to simply set the same passphrase. [Return to list](#list-of-options) @@ -911,7 +914,7 @@ For File mode: Default value is 0 and it's recommended not to be changed. | -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_PBKEYLEN` | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | -Sender encryption key length. +Encryption key length. Possible values: diff --git a/srtcore/srt.h b/srtcore/srt.h index f3a5becdc..f0a8b233a 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -206,8 +206,8 @@ typedef enum SRT_SOCKOPT { SRTO_LATENCY = 23, // NOT RECOMMENDED. SET: to both SRTO_RCVLATENCY and SRTO_PEERLATENCY. GET: same as SRTO_RCVLATENCY. SRTO_INPUTBW = 24, // Estimated input stream rate. SRTO_OHEADBW, // MaxBW ceiling based on % over input stream rate. Applies when UDT_MAXBW=0 (auto). - SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto - SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (128-bit) + SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase (must be 10..79 characters, or empty to disable encryption) + SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (AES-128) SRTO_KMSTATE, // Key Material exchange status (UDT_SRTKmState) SRTO_IPTTL = 29, // IP Time To Live (passthru for system sockopt IPPROTO_IP/IP_TTL) SRTO_IPTOS, // IP Type of Service (passthru for system sockopt IPPROTO_IP/IP_TOS) From 6ff3225f9c62d6a683fa39b281b2fa68947fdf75 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 14 Dec 2020 18:02:39 +0100 Subject: [PATCH 007/790] [docs] Fixed formatting of API.md --- docs/API.md | 658 ++++++++++++++++++++++++++-------------------------- 1 file changed, 324 insertions(+), 334 deletions(-) diff --git a/docs/API.md b/docs/API.md index 182ecc41a..8e5a9f3cd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,66 +1,63 @@ # SRT API -The SRT C API (defined in `srt.h` file) is largely based in design on the legacy -UDT API, with some important changes. The `udt.h` file contains the legacy UDT API -plus some minor optional functions that require the C++ standard library to be used. -There are a few optional C++ API functions stored there, as there is no real C++ API +The SRT C API (defined in `srt.h` file) is largely based in design on the legacy +UDT API, with some important changes. The `udt.h` file contains the legacy UDT API +plus some minor optional functions that require the C++ standard library to be used. +There are a few optional C++ API functions stored there, as there is no real C++ API for SRT. These functions may be useful in certain situations. -There are some example applications so that you can see how the API is being used, -including `srt-live-transmit` and `srt-file-transmit`. All SRT related material is contained +There are some example applications so that you can see how the API is being used, +including `srt-live-transmit` and `srt-file-transmit`. All SRT related material is contained in `transmitmedia.*` files in the `apps` directory -which is used by all applications. See `SrtSource::Read` and `SrtTarget::Write` +which is used by all applications. See `SrtSource::Read` and `SrtTarget::Write` as examples of how data are read and written in SRT. - - [Setup and teardown](#setup-and-teardown) - - [Creating and destroying a socket](#creating-and-destroying-a-socket) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Important Remarks](#important-remarks) - - [Binding and connecting](#binding-and-connecting) - - [Synopsis](#synopsis) - - [SRT Usage - listener (server)](#srt-usage---listener-server) - - [SRT Usage - rendezvous](#srt-usage---rendezvous) - - [Sending and Receiving](#sending-and-receiving) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Transmission types available in SRT](#transmission-types-available-in-srt) - - [Blocking and Non-blocking Mode](#blocking-and-non-blocking-mode) - - [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events)) - - [Synopsis](#synopsis) - - [SRT Usage](#srt-usage) - - [Transmission types](#transmission-types) - - [Terminology](#terminology) - - [Transmission method: Live](#transmission-method-live) - - [Transmission method: Buffer](#transmission-method-buffer) - - [Transmission method: Message](#transmission-method-message) - - -**NOTE**: The socket option descriptions originally contained in this document -have been moved to [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md) - - -Setup and teardown -================== +- [Setup and teardown](#setup-and-teardown) +- [Creating and destroying a socket](#creating-and-destroying-a-socket) + - [Synopsis](#synopsis) + - [Usage](#usage) + - [Important Remarks](#important-remarks) +- [Binding and connecting](#binding-and-connecting) + - [Synopsis](#synopsis) + - [SRT Usage - listener (server)](#srt-usage---listener-server) + - [SRT Usage - rendezvous](#srt-usage---rendezvous) +- [Sending and Receiving](#sending-and-receiving) + - [Synopsis](#synopsis) + - [Usage](#usage) + - [Transmission types available in SRT](#transmission-types-available-in-srt) +- [Blocking and Non-blocking Mode](#blocking-and-non-blocking-mode) +- [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events)) + - [Synopsis](#synopsis) + - [SRT Usage](#srt-usage) + - [Transmission types](#transmission-types) + - [Terminology](#terminology) + - [Transmission method: Live](#transmission-method-live) + - [Transmission method: Buffer](#transmission-method-buffer) + - [Transmission method: Message](#transmission-method-message) + +**NOTE**: The socket option descriptions originally contained in this document +have been moved to [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md). + +## Setup and teardown Before any part of the SRT C API can be used, the user should call the `srt_startup()` function. Likewise, before the application exits, the `srt_cleanup()` function -should be called. Note that one of the things the startup function does is to create +should be called. Note that one of the things the startup function does is to create a new thread, so choose the point of execution for these functions carefully. -Creating and destroying a socket -================================ +## Creating and destroying a socket -To do anything with SRT, you first have to create an SRT socket. The term "socket" -in this case is used because of its logical similarity to system-wide sockets. -An SRT socket is not directly related to system sockets, but like a system socket +To do anything with SRT, you first have to create an SRT socket. The term "socket" +in this case is used because of its logical similarity to system-wide sockets. +An SRT socket is not directly related to system sockets, but like a system socket it is used to define a point of communication. -Synopsis --------- +### Synopsis - SRTSOCKET srt_socket(int af, int, int); - int srt_close(SRTSOCKET s); +```c++ +SRTSOCKET srt_socket(int af, int, int); +int srt_close(SRTSOCKET s); +``` The `srt_socket` function is based on the legacy UDT API except the first parameter. The other two are ignored. @@ -68,23 +65,24 @@ the first parameter. The other two are ignored. Note that `SRTSOCKET` is just an alias for `int`; this is a legacy naming convention from UDT, which is here only for clarity. -Usage ------ +### Usage - sock = srt_socket(AF_INET, SOCK_DGRAM, 0); +```c++ +sock = srt_socket(AF_INET, SOCK_DGRAM, 0); +``` This creates a socket, which can next be configured and then used for communication. - srt_close(sock); +```c++ +srt_close(sock); +``` This closes the socket and frees all its resources. Note that the true life of the socket does not end exactly after this function exits - some details are being finished in a separate "SRT GC" thread. Still, at least all shared system resources (such as listener port) should be released after this function exits. - -Important Remarks ------------------ +### Important Remarks 1. Please note that the use of SRT with `AF_INET6` has not been fully tested; use at your own risk. @@ -98,19 +96,18 @@ port". However SRT offers more flexibility than UDP (or TCP, the more logical similarity) because it manages ports as its own resources. For example, one port may be shared between various services. - -Binding and connecting -====================== +## Binding and connecting Connections are established using the same philosophy as TCP, using functions with names and signatures similar to the BSD Socket API. What is new here is the _rendezvous_ mode. -Synopsis --------- +### Synopsis - int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); - int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock); +```c++ +int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); +int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock); +``` This function sets up the "sockname" for the socket, that is, the local IP address of the network device (use `INADDR_ANY` for using any device) and port. Note that @@ -121,21 +118,27 @@ function (or use port number 0), a unique port number will be selected automatic The `*_peerof` version simply copies the bound address setting from an existing UDP socket. - int srt_listen(SRTSOCKET u, int backlog); +```c++ +int srt_listen(SRTSOCKET u, int backlog); +``` This sets the backlog (maximum allowed simultaneously pending connections) and puts the socket into a listening state -- that is, incoming connections will be accepted in the call `srt_accept`. - SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen); +```c++ +SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen); +``` This function accepts the incoming connection (the peer should do `srt_connect`) and returns a socket that is exclusively bound to an opposite socket at the peer. The peer's address is returned in the `addr` argument. - int srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); - int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +```c++ +int srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); +int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +``` This function initiates the connection of a given socket with its peer's counterpart (the peer gets the new socket for this connection from `srt_accept`). The @@ -143,8 +146,10 @@ address for connection is passed in 'name'. The `connect_debug` version allows for enforcing the ISN (initial sequence number); this is used only for debugging or unusual experiments. - int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, - const struct sockaddr* remote_name, int remote_namelen); +```c++ +int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, + const struct sockaddr* remote_name, int remote_namelen); +``` A convenience function that combines the calls to bind, setting the `SRTO_RENDEZVOUS` flag, and connecting to the rendezvous counterpart. For simplest usage, the `local_name` should @@ -153,52 +158,52 @@ and `remote_name` must use the same port. The peer to which this is going to con should call the same function, with appropriate local and remote addresses. A rendezvous connection means that both parties connect to one another simultaneously. +### Listener (Server) Example + +```c++ +sockaddr_in sa = { ... }; // set local listening port and possibly interface's IP +int st = srt_bind(sock, (sockaddr*)&sa, sizeof sa); +srt_listen(sock, 5); +while ( !finish ) { + int sa_len = sizeof sa; + newsocket = srt_accept(sock, (sockaddr*)&sa, &sa_len); + HandleNewClient(newsocket, sa); +} +``` +### Caller (Client) Example -SRT Usage - listener (server) ------------------------------ - - sockaddr_in sa = { ... }; // set local listening port and possibly interface's IP - int st = srt_bind(sock, (sockaddr*)&sa, sizeof sa); - srt_listen(sock, 5); - while ( !finish ) { - int sa_len = sizeof sa; - newsocket = srt_accept(sock, (sockaddr*)&sa, &sa_len); - HandleNewClient(newsocket, sa); - } - -SRT Usage - caller (client) ---------------------------- - - sockaddr_in sa = { ... }; // set target IP and port - - int st = srt_connect(sock, (sockaddr*)&sa, sizeof sa); - HandleConnection(sock); +```c++ +sockaddr_in sa = { ... }; // set target IP and port +int st = srt_connect(sock, (sockaddr*)&sa, sizeof sa); +HandleConnection(sock); +``` -SRT Usage - rendezvous ----------------------- +### Rendezvous Example - sockaddr_in lsa = { ... }; // set local listening IP/port - sockaddr_in rsa = { ... }; // set remote IP/port +```c++ +sockaddr_in lsa = { ... }; // set local listening IP/port +sockaddr_in rsa = { ... }; // set remote IP/port - srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); - int stb = srt_bind(sock, (sockaddr*)&lsa, sizeof lsa); - int stc = srt_connect(sock, (sockaddr*)&rsa, sizeof rsa); - HandleConnection(sock); +srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); +int stb = srt_bind(sock, (sockaddr*)&lsa, sizeof lsa); +int stc = srt_connect(sock, (sockaddr*)&rsa, sizeof rsa); +HandleConnection(sock); +``` or simpler - sockaddr_in lsa = { ... }; // set local listening IP/port - sockaddr_in rsa = { ... }; // set remote IP/port - - int stc = srt_rendezvous(sock, (sockaddr*)&lsa, sizeof lsa, - (sockaddr*)&rsa, sizeof rsa); - HandleConnection(sock); +```c++ +sockaddr_in lsa = { ... }; // set local listening IP/port +sockaddr_in rsa = { ... }; // set remote IP/port +int stc = srt_rendezvous(sock, (sockaddr*)&lsa, sizeof lsa, + (sockaddr*)&rsa, sizeof rsa); +HandleConnection(sock); +``` -Sending and Receiving -===================== +## Sending and Receiving The SRT API for sending and receiving is split into three categories: *simple*, *rich*, and *for files only*. @@ -218,95 +223,92 @@ Functions with the `msg2` suffix use the `SRT_MSGCTRL` object, and have the following interpretation (except `flags` and `boundary` which are reserved for future use and should be 0): -* `srt_sendmsg2`: - * `msgttl`: [IN] maximum time (in ms) to wait for successful delivery (-1: indefinitely) - * `inorder`: [IN] if false, the later sent message is allowed to be delivered earlier - * `srctime`: [IN] timestamp to be used for sending (0 if current time) - * `pktseq`: unused - * `msgno`: [OUT] message number assigned to the currently sent message - -* `srt_recvmsg2` - * `msgttl`: unused - * `inorder`: unused - * `srctime`: [OUT] timestamp set for this dataset when sending - * `pktseq`: [OUT] packet sequence number (first packet from the message, if it spans multiple UDP packets) - * `msgno`: [OUT] message number assigned to the currently received message - -Please note that the `msgttl` and `inorder` arguments and fields in `SRT_MSGCTRL` -are meaningful only when you use the message API in file mode (this will be explained -later). In live mode, which is the SRT default, packets are always delivered when -the time comes (always in order), where you don't want a packet to be dropped +- `srt_sendmsg2`: + - `msgttl`: [IN] maximum time (in ms) to wait for successful delivery (-1: indefinitely) + - `inorder`: [IN] if false, the later sent message is allowed to be delivered earlier + - `srctime`: [IN] timestamp to be used for sending (0 if current time) + - `pktseq`: unused + - `msgno`: [OUT] message number assigned to the currently sent message + +- `srt_recvmsg2` + - `msgttl`: unused + - `inorder`: unused + - `srctime`: [OUT] timestamp set for this dataset when sending + - `pktseq`: [OUT] packet sequence number (first packet from the message, if it spans multiple UDP packets) + - `msgno`: [OUT] message number assigned to the currently received message + +Please note that the `msgttl` and `inorder` arguments and fields in `SRT_MSGCTRL` +are meaningful only when you use the message API in file mode (this will be explained +later). In live mode, which is the SRT default, packets are always delivered when +the time comes (always in order), where you don't want a packet to be dropped before sending (so -1 should be passed here). The `srctime` parameter is an SRT addition for applications (i.e. gateways) -forwarding SRT streams. It permits pulling and pushing of the sender's original -time stamp, converted to local time and drift adjusted. The `srctime` parameter -is the number of usec (since epoch) in local SRT clock time. If the connection -is not between SRT peers or if **Timestamp-Based Packet Delivery mode (TSBPDMODE)** -is not enabled (see [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md)), -the extracted `srctime` will be 0. Passing `srctime = 0` in `sendmsg` is like using -the API without `srctime` and the local send time will be used (if TSBPDMODE is +forwarding SRT streams. It permits pulling and pushing of the sender's original +time stamp, converted to local time and drift adjusted. The `srctime` parameter +is the number of usec (since epoch) in local SRT clock time. If the connection +is not between SRT peers or if **Timestamp-Based Packet Delivery mode (TSBPDMODE)** +is not enabled (see [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md)), +the extracted `srctime` will be 0. Passing `srctime = 0` in `sendmsg` is like using +the API without `srctime` and the local send time will be used (if TSBPDMODE is enabled and receiver supports it). +### Synopsis -Synopsis --------- +```c++ +int srt_send(SRTSOCKET s, const char* buf, int len); +int srt_sendmsg(SRTSOCKET s, const char* buf, int len, int msgttl, bool inorder, uint64_t srctime); +int srt_sendmsg2(SRTSOCKET s, const char* buf, int len, SRT_MSGCTRL* msgctrl); - int srt_send(SRTSOCKET s, const char* buf, int len); - int srt_sendmsg(SRTSOCKET s, const char* buf, int len, int msgttl, bool inorder, uint64_t srctime); - int srt_sendmsg2(SRTSOCKET s, const char* buf, int len, SRT_MSGCTRL* msgctrl); - - int srt_recv(SRTSOCKET s, char* buf, int len); - int srt_recvmsg(SRTSOCKET s, char* buf, int len); - int srt_recvmsg2(SRTSOCKET s, char* buf, int len, SRT_MSGCTRL* msgctrl); +int srt_recv(SRTSOCKET s, char* buf, int len); +int srt_recvmsg(SRTSOCKET s, char* buf, int len); +int srt_recvmsg2(SRTSOCKET s, char* buf, int len, SRT_MSGCTRL* msgctrl); +``` -Usage ------ +### Usage Sending a payload: - nb = srt_sendmsg(u, buf, nb, -1, true); +```c++ +nb = srt_sendmsg(u, buf, nb, -1, true); - nb = srt_send(u, buf, nb); - - SRT_MSGCTRL mc = srt_msgctrl_default; - nb = srt_sendmsg2(u, buf, nb, &mc); +nb = srt_send(u, buf, nb); +SRT_MSGCTRL mc = srt_msgctrl_default; +nb = srt_sendmsg2(u, buf, nb, &mc); +``` Receiving a payload: - nb = srt_recvmsg(u, buf, nb); - nb = srt_recv(u, buf, nb); - - SRT_MSGCTRL mc = srt_msgctrl_default; - nb = srt_recvmsg2(u, buf, nb, &mc); +```c++ +nb = srt_recvmsg(u, buf, nb); +nb = srt_recv(u, buf, nb); +SRT_MSGCTRL mc = srt_msgctrl_default; +nb = srt_recvmsg2(u, buf, nb, &mc); +``` -Transmission types available in SRT ------------------------------------ +### Transmission types available in SRT -Mode settings determine how the sender and receiver functions work. The main -[socket options](APISocketOptions.md) -that control it are: +Mode settings determine how the sender and receiver functions work. The main +[socket options](APISocketOptions.md) that control it are: -* `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected +- `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected mode: - * `SRTT_LIVE` (default) the Live mode (for live stream transmissions) - * `SRTT_FILE` the File mode (for "no time controlled" fastest data transmission) -* `SRTO_MESSAGEAPI` - * true: (default in Live mode): use Message API - * false: (default in File mode): use Buffer API + - `SRTT_LIVE` (default) the Live mode (for live stream transmissions) + - `SRTT_FILE` the File mode (for "no time controlled" fastest data transmission) +- `SRTO_MESSAGEAPI` + - true: (default in Live mode): use Message API + - false: (default in File mode): use Buffer API See [Transmission types](#transmission-types) below. - -Blocking and Non-blocking Mode -============================== +## Blocking and Non-blocking Mode SRT functions can also work in blocking and non-blocking mode, for which there are two separate options for sending and receiving: `SRTO_SNDSYN` and `SRTO_RCVSYN`. When blocking mode is used, a function will not exit until -the availability condition is satisfied. In non-blocking mode the function +the availability condition is satisfied. In non-blocking mode the function always exits immediately, and in case of lack of resource availability, it returns an error with an appropriate code. The use of non-blocking mode usually requires using some polling mechanism, which in SRT is **EPoll**. @@ -316,28 +318,25 @@ and receiving. For example, `SNDSYN` defines blocking for `srt_connect` and `RCVSYN` defines blocking for `srt_accept`. The `SNDSYN` also makes `srt_close` exit only after the sending buffer is completely empty. - -EPoll (Non-blocking Mode Events) -================================ +## EPoll (Non-blocking Mode Events) EPoll is a mechanism to track the events happening on the sockets, both "system sockets" (see `SYSSOCKET` type) and SRT Sockets. Note that `SYSSOCKET` is also an alias for `int`, used only for clarity. +### Synopsis -Synopsis --------- - - int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); - int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL); - int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, - int64_t msTimeOut, - SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum); - int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); - int srt_epoll_clear_usocks(int eid); +```c++ +int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); +int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL); +int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, + int64_t msTimeOut, + SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum); +int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); +int srt_epoll_clear_usocks(int eid); +``` -SRT Usage ---------- +### SRT Usage SRT socket being a user level concept, the system epoll (or other select) cannot be used to handle SRT non-blocking mode events. Instead, SRT provides a @@ -367,22 +366,24 @@ is raised on any of the subscribed sockets. This function will exit as soon as at least one event is detected or a timeout occurs. The timeout is specified in `[ms]`, with two special values: - - 0: check and report immediately (don't wait) - - -1: wait indefinitely (not interruptible, even by a system signal) +- 0: check and report immediately (don't wait) +- -1: wait indefinitely (not interruptible, even by a system signal) There are some differences in the synopsis between these two: -1. `srt_epoll_wait`: Both system and SRT sockets can be subscribed. This +#### `srt_epoll_wait` + +Both system and SRT sockets can be subscribed. This function reports events on both socket types according to subscriptions, in these arrays: - - `readfds` and `lrfds`: subscribed for `IN` and `ERR` - - `writefds` and `lwfds`: subscribed for `OUT` and `ERR` +- `readfds` and `lrfds`: subscribed for `IN` and `ERR` +- `writefds` and `lwfds`: subscribed for `OUT` and `ERR` where: - - `readfds` and `writefds` report SRT sockets ("user" socket) - - `lrfds` and `lwfds` report system sockets +- `readfds` and `writefds` report SRT sockets ("user" socket) +- `lrfds` and `lwfds` report system sockets **NOTE**: this function provides no straightforward possibility to report sockets with an error. If you want to distinguish a report of readiness @@ -393,8 +394,8 @@ in the array in the direction for which the socket wasn't subscribed. For example, when an SRT socket is subscribed for `SRT_EPOLL_OUT | SRT_EPOLL_ERR`, its presence in `readfds` means that an error is reported for it. This need not be a big problem, because when an error is reported on -a socket, making it appear as if it were ready for an operation, then when that -operation occurs it will simply result in an error. You can use this as an +a socket, making it appear as if it were ready for an operation, then when that +operation occurs it will simply result in an error. You can use this as an alternative error check method. This function also reports an error of type `SRT_ETIMEOUT` when no socket is @@ -407,13 +408,14 @@ when system sockets are involved, is also 10ms. The return time from a poll function can only be quicker when there is an event raised on one of the active SRT sockets. +### `srt_epoll_uwait` -2. `srt_epoll_uwait`: In this function only the SRT sockets can be subscribed +In this function only the SRT sockets can be subscribed (it reports error if you pass an epoll id that is subscribed to system sockets). This function waits for the first event on subscribed SRT sockets and reports all events collected at that moment in an array with the following structure: -``` +```c++ typedef struct SRT_EPOLL_EVENT_ { SRTSOCKET fd; @@ -433,112 +435,105 @@ the epoll container. The SRT EPoll system does not supports all features of Linux epoll. For example, it only supports level-triggered events for system sockets. +### Transmission types -Transmission types ------------------- - -SRT was originally intended to be used for Live Streaming and therefore its main +SRT was originally intended to be used for Live Streaming and therefore its main and default transmission type is "live". However, SRT supports the modes that the original UDT library supported, that is, *file* and *message* transmission. -There are two general modes: **Live** and **File** transmission. Inside File -transmission mode, there are also two possibilities: **Buffer API** and -**Message API**. The Live mode uses Message API. However it doesn't exactly match -the description of the Message API because it uses a maximum single sending buffer +There are two general modes: **Live** and **File** transmission. Inside File +transmission mode, there are also two possibilities: **Buffer API** and +**Message API**. The Live mode uses Message API. However it doesn't exactly match +the description of the Message API because it uses a maximum single sending buffer up to the size that fits in one UDP packet. There are two options to set a particular type: -* `SRTO_TRANSTYPE`: uses the enum value with `SRTT_LIVE` for live mode - and `SRTT_FILE` for file mode. This option actually changes several parameters - to their default values for that mode. After this is done, additional parameters, +- `SRTO_TRANSTYPE`: uses the enum value with `SRTT_LIVE` for live mode + and `SRTT_FILE` for file mode. This option actually changes several parameters + to their default values for that mode. After this is done, additional parameters, including those that are set here, can be further changed. -* `SRTO_MESSAGEAPI`: This sets the Message API (true) or Buffer API (false) + +- `SRTO_MESSAGEAPI`: This sets the Message API (true) or Buffer API (false) This makes possible a total of three data transmission methods: -* [Live](#transmission-method-live) -* [Buffer](#transmission-method-buffer) -* [Message](#transmission-method-message) +- [Live](#transmission-method-live) +- [Buffer](#transmission-method-buffer) +- [Message](#transmission-method-message) ### Terminology The following terms are used in the description of transmission types: **HANGUP / RESUME**: These terms have different meanings depending on the blocking -state. They describe how a particular function behaves when performing an operation +state. They describe how a particular function behaves when performing an operation requires a specific readiness condition to be satisfied. -In blocking mode HANGUP means that the function blocks until a condition is -satisfied. RESUME means that the condition is satisfied and the function performs +In blocking mode HANGUP means that the function blocks until a condition is +satisfied. RESUME means that the condition is satisfied and the function performs the required operation. -In non-blocking mode the only difference is that HANGUP, instead of blocking, makes +In non-blocking mode the only difference is that HANGUP, instead of blocking, makes the function exit immediately with an appropriate error code (such as SRT_EASYNC*, -SRT_ETIMEOUT or SRT_ECONGEST) explaining why the function is not ready to perform -the operation. Refer to the error descriptions in [API-funtions.md](API-funtions.md) +SRT_ETIMEOUT or SRT_ECONGEST) explaining why the function is not ready to perform +the operation. Refer to the error descriptions in [API-funtions.md](API-funtions.md) for details. The following types of operations are involved: 1. Reading data: `srt_recv`, `srt_recvmsg`, `srt_recvmsg2`, `srt_recvfile`. - -The function HANGS UP if there are no available data to read, and RESUMES when -readable data become available (`SRT_EPOLL_IN` flag set in epoll). Use `SRTO_RCVSYN` -to control blocking mode here. + The function HANGS UP if there are no available data to read, and RESUMES when + readable data become available (`SRT_EPOLL_IN` flag set in epoll). Use `SRTO_RCVSYN` + to control blocking mode here. 2. Writing data: `srt_send`, `srt_sendmsg`, `srt_sendmsg2`, `srt_sendfile`. - -The function HANGS UP if the sender buffer becomes full and unable to store -any additional data, and RESUMES if the data scheduled for sending have been -removed from the sender buffer (after being sent and acknowledged) and there -is enough free space in the sender buffer to store data (`SRT_EPOLL_OUT` flag -set in epoll). Use `SRTO_SNDSYN` to control blocking mode here. + The function HANGS UP if the sender buffer becomes full and unable to store + any additional data, and RESUMES if the data scheduled for sending have been + removed from the sender buffer (after being sent and acknowledged) and there + is enough free space in the sender buffer to store data (`SRT_EPOLL_OUT` flag + set in epoll). Use `SRTO_SNDSYN` to control blocking mode here. 3. Accepting an incoming connection: `srt_accept` - -The function HANGS UP if there are no new connections reporting in, and -RESUMES when a new connection has been processed and a new socket or group -has been created to handle it. Note that this function requires the listener -socket to get the connection (the flag `SRTO_RCVSYN` set on -the listener socket controls the blocking mode for this operation). Note also -that the blocking mode for a similar `srt_accept_bond` function is controlled -exclusively by its timeout parameter because it can work with multiple listener -sockets, potentially with different settings. + The function HANGS UP if there are no new connections reporting in, and + RESUMES when a new connection has been processed and a new socket or group + has been created to handle it. Note that this function requires the listener + socket to get the connection (the flag `SRTO_RCVSYN` set on + the listener socket controls the blocking mode for this operation). Note also + that the blocking mode for a similar `srt_accept_bond` function is controlled + exclusively by its timeout parameter because it can work with multiple listener + sockets, potentially with different settings. 4. Connecting: `srt_connect` and its derivatives - -The function HANGS UP in the beginning, and RESUMES when the socket used for -connecting is either ready to perform transmission operations or has failed to -connect. It behaves a little differently in non-blocking mode -- the function -should be called only once, and it simply returns a success result as a "HANGUP". -Calling it again with the same socket would be an error. Calling it with a group -would start a completely new connection. It is only possible to determine whether -an operation has finished ("has RESUMED") from epoll flags. The socket, when -successfully connected, would have `SRT_EPOLL_OUT` set, that is, becomes ready -to send data, and `SRT_EPOLL_ERR` when it failed to connect. - -**BLIND / FAST / LATE REXMIT**: BLIND REXMIT is a situation where packets that -were sent are still not acknowledged, either in the expected time frame, or when -another ACK has come for the same number, but no packets have been reported as -lost, or at least not for all still unacknowledged packets. The congestion control -class is responsible for the algorithm for taking care of this situation, which is + The function HANGS UP in the beginning, and RESUMES when the socket used for + connecting is either ready to perform transmission operations or has failed to + connect. It behaves a little differently in non-blocking mode -- the function + should be called only once, and it simply returns a success result as a "HANGUP". + Calling it again with the same socket would be an error. Calling it with a group + would start a completely new connection. It is only possible to determine whether + an operation has finished ("has RESUMED") from epoll flags. The socket, when + successfully connected, would have `SRT_EPOLL_OUT` set, that is, becomes ready + to send data, and `SRT_EPOLL_ERR` when it failed to connect. + +**BLIND / FAST / LATE REXMIT**: BLIND REXMIT is a situation where packets that +were sent are still not acknowledged, either in the expected time frame, or when +another ACK has come for the same number, but no packets have been reported as +lost, or at least not for all still unacknowledged packets. The congestion control +class is responsible for the algorithm for taking care of this situation, which is either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. - -Transmission method: Live -------------------------- +### Transmission method: Live Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](APISocketOptions.md): -* `SRTO_TSBPDMODE` = true -* `SRTO_RCVLATENCY` = 120 -* `SRTO_PEERLATENCY` = 0 -* `SRTO_TLPKTDROP` = true -* `SRTO_MESSAGEAPI` = true -* `SRTO_NAKREPORT` = true -* `SRTO_PAYLOADSIZE` = 1316 -* `SRTO_CONGESTION` = "live" +- `SRTO_TSBPDMODE` = true +- `SRTO_RCVLATENCY` = 120 +- `SRTO_PEERLATENCY` = 0 +- `SRTO_TLPKTDROP` = true +- `SRTO_MESSAGEAPI` = true +- `SRTO_NAKREPORT` = true +- `SRTO_PAYLOADSIZE` = 1316 +- `SRTO_CONGESTION` = "live" In this mode, every call to a sending function is allowed to send only so much data, as declared by `SRTO_PAYLOADSIZE`, whose value is still @@ -553,10 +548,10 @@ the time comes for the packet to "play"). This mode uses the `LiveCC` congestion control class, which puts only a slight limitation on the bandwidth, if needed (i.e. by adding extra time if the interval -between two consecutive packets would otherwise be too short for the defined speed -limit). Note that it is not intended to work with "virtually infinite" ingest -speeds (such as, for example, reading directly from a file). Therefore the -application is not allowed to stream data with maximum speed -- it must take care +between two consecutive packets would otherwise be too short for the defined speed +limit). Note that it is not intended to work with "virtually infinite" ingest +speeds (such as, for example, reading directly from a file). Therefore the +application is not allowed to stream data with maximum speed -- it must take care that the speed of data being sent is in rhythm with timestamps in the live stream. Otherwise the behavior is undefined and might be surprisingly disappointing. @@ -567,57 +562,55 @@ lost packets (if TSBPD mode is off - see [`SRTO_TSBPDMODE`](APISocketOptions.md# You may wish to tweak some of the parameters below: -* `SRTO_TSBPDMODE`: You can turn off controlled latency if your application uses +- `SRTO_TSBPDMODE`: You can turn off controlled latency if your application uses its own method of latency control. -* `SRTO_RCVLATENCY`: You can increase the latency time, if this is +- `SRTO_RCVLATENCY`: You can increase the latency time, if this is too short. Setting a shorter latency than the default is strongly discouraged, although in some very specific and dedicated networks this may still be reasonable. Note that `SRTO_PEERLATENCY` is an option for the sending party, which is the minimum possible value for a receiver. -* `SRTO_TLPKTDROP`: When true (default), this will drop the packets +- `SRTO_TLPKTDROP`: When true (default), this will drop the packets that haven't been retransmitted on time, that is, before the next packet -that is already received becomes ready to play. You can turn this off to always +that is already received becomes ready to play. You can turn this off to always ensure a clean delivery. However, a lost packet can simply pause a delivery for some longer, potentially undefined time, and cause even worse tearing for the player. Setting higher latency will help much more in the case when TLPKTDROP causes packet drops too often. -* `SRTO_NAKREPORT`: Turns on repeated sending of loss reports, when the lost +- `SRTO_NAKREPORT`: Turns on repeated sending of loss reports, when the lost packet was not recovered quickly enough, which raises suspicions that the loss report itself was lost. Without it, the loss report will be always reported just once and never repeated again, and then the lost payload packet will be probably dropped by the TLPKTDROP mechanism. -* `SRTO_PAYLOADSIZE`: Default value is for MPEG TS. If you are going -to use SRT to send any different kind of payload, such as, for example, +- `SRTO_PAYLOADSIZE`: Default value is for MPEG TS. If you are going +to use SRT to send any different kind of payload, such as, for example, wrapping a live stream in very small frames, then you can use a bigger maximum frame size, though not greater than 1456 bytes. -Parameters from the modified for transmission type list, not mentioned in the +Parameters from the modified for transmission type list, not mentioned in the list above, are crucial for Live mode and shall not be changed. -The BLIND REXMIT situation is resolved using the FASTREXMIT algorithm by LiveCC: -sending non-acknowledged packets blindly on the premise that the receiver lingers -too long before acknowledging them. This mechanism isn't used (i.e. the BLIND REXMIT -situation isn't handled at all) when `SRTO_NAKREPORT` is set by the peer -- the +The BLIND REXMIT situation is resolved using the FASTREXMIT algorithm by LiveCC: +sending non-acknowledged packets blindly on the premise that the receiver lingers +too long before acknowledging them. This mechanism isn't used (i.e. the BLIND REXMIT +situation isn't handled at all) when `SRTO_NAKREPORT` is set by the peer -- the NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. - -Transmission method: Buffer ---------------------------- +### Transmission method: Buffer Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](APISocketOptions.md): -* `SRTO_TSBPDMODE` = false -* `SRTO_RCVLATENCY` = 0 -* `SRTO_PEERLATENCY` = 0 -* `SRTO_TLPKTDROP` = false -* `SRTO_MESSAGEAPI` = false -* `SRTO_NAKREPORT` = false -* `SRTO_PAYLOADSIZE` = 0 -* `SRTO_CONGESTION` = "file" +- `SRTO_TSBPDMODE` = false +- `SRTO_RCVLATENCY` = 0 +- `SRTO_PEERLATENCY` = 0 +- `SRTO_TLPKTDROP` = false +- `SRTO_MESSAGEAPI` = false +- `SRTO_NAKREPORT` = false +- `SRTO_PAYLOADSIZE` = 0 +- `SRTO_CONGESTION` = "file" In this mode, calling a sending function is allowed to potentially send virtually any size of data. The sending function will HANGUP only if the @@ -631,16 +624,16 @@ From the receiving function there will be retrieved as many data as the minimum of the passed buffer size and available data; data still available and not retrieved by this call will be available for retrieval in the next call. -There is also a dedicated pair of functions that can only be used in this mode: -`srt_sendfile` and `srt_recvfile`. These functions can be used to transmit the +There is also a dedicated pair of functions that can only be used in this mode: +`srt_sendfile` and `srt_recvfile`. These functions can be used to transmit the whole file, or a fragment of it, based on the offset and size. -This mode uses the `FileCC` congestion control class, which is a direct copy of +This mode uses the `FileCC` congestion control class, which is a direct copy of UDT's `CUDTCC` congestion control class, adjusted to the needs of SRT's congestion control framework. This class generally sends the data with maximum speed in the beginning, until the flight window is full, and then keeps the speed at the edge of the flight window, only slowing down in the case where -packet loss was detected. The bandwidth usage can be directly limited by the +packet loss was detected. The bandwidth usage can be directly limited by the `SRTO_MAXBW` option. The BLIND REXMIT situation is resolved in FileCC using the LATEREXMIT @@ -649,71 +642,68 @@ loss list is empty and the flight window is full, all packets since the last ACK are sent again (that's more or less the TCP behavior, but in contrast to TCP, this is done as a very low probability fallback). -Most of the parameters described above have `false` or `0` values as they usually -designate features used in Live mode. None are used with File mode. The only option -that makes sense to modify after the `SRTT_FILE` type was set is `SRTO_MESSAGEAPI`, +Most of the parameters described above have `false` or `0` values as they usually +designate features used in Live mode. None are used with File mode. The only option +that makes sense to modify after the `SRTT_FILE` type was set is `SRTO_MESSAGEAPI`, which is described below. - -Transmission method: Message ----------------------------- +### Transmission method: Message Setting `SRTO_TRANSTYPE` to `SRTT_FILE` and then setting `SRTO_MESSAGEAPI` to `true` implies usage of the Message transmission method. Parameters are set as -described above for the Buffer method, with the exception of `SRTO_MESSAGEAPI`. +described above for the Buffer method, with the exception of `SRTO_MESSAGEAPI`. The "file" congestion controller is also used in this mode. It differs from the Buffer method, however, in terms of the rules concerning sending and receiving. **HISTORICAL INFO**: The library on which SRT was based (UDT) somewhat misleadingly -used the terms `STREAM` and `DGRAM`, and used the system symbols `SOCK_STREAM` and -`SOCK_DGRAM` in the socket creation function. A "datagram" in the UDT terminology -has nothing to do with the "datagram" term in networking terminology, where its -size is limited to as much it can fit in one MTU. In UDT it is actually a message, -which may span multiple UDP packets and has clearly defined boundaries. It's rather -similar to the **SCTP** protocol. Also, in UDP the API functions were strictly bound +used the terms `STREAM` and `DGRAM`, and used the system symbols `SOCK_STREAM` and +`SOCK_DGRAM` in the socket creation function. A "datagram" in the UDT terminology +has nothing to do with the "datagram" term in networking terminology, where its +size is limited to as much it can fit in one MTU. In UDT it is actually a message, +which may span multiple UDP packets and has clearly defined boundaries. It's rather +similar to the **SCTP** protocol. Also, in UDP the API functions were strictly bound to `DGRAM` or `STREAM` mode: `UDT::send/UDT::recv` were only for `STREAM` and -`UDT::sendmsg/UDT::recvmsg` only for `DGRAM`. In SRT this is changed: all functions -can be used in all modes, except `srt_sendfile/srt_recvfile`, and how the functions +`UDT::sendmsg/UDT::recvmsg` only for `DGRAM`. In SRT this is changed: all functions +can be used in all modes, except `srt_sendfile/srt_recvfile`, and how the functions actually work is controlled by the `SRTO_MESSAGEAPI` flag. -In message mode, every sending function sends **exactly** as much data as it is -passed in a single sending function call. The receiver also receives not less than -**exactly** the number of bytes that was sent (although every message may have a +In message mode, every sending function sends **exactly** as much data as it is +passed in a single sending function call. The receiver also receives not less than +**exactly** the number of bytes that was sent (although every message may have a different size). Every message may also have extra parameters: - - **TTL** defines how much time (in ms) the message should wait in the sending - buffer for the opportunity to be picked up by the sender thread and sent over - the network; otherwise it is dropped. Note that this TTL only applies to packets that - have been lost and should be retransmitted. - - - **INORDER**, when true, means the messages must be read by the receiver in - exactly the same order in which they were sent. In the situation where a message - suffers a packet loss, this prevents any subsequent messages from achieving - completion status prior to recovery of the preceding message. - -The sending function will HANGUP when the free space in the sending buffer does -not exactly fit the whole message, and it will only RESUME if the free space in -the sending buffer grows up to this size. The call to the sending function also -returns with an error when the size of the message exceeds the total size of the -buffer (this can be modified by the `SRTO_SNDBUF` option). In other words, it is -not designed to send just a part of the message -- either the whole message is +- **TTL** defines how much time (in ms) the message should wait in the sending +buffer for the opportunity to be picked up by the sender thread and sent over +the network; otherwise it is dropped. Note that this TTL only applies to packets that +have been lost and should be retransmitted. + +- **INORDER**, when true, means the messages must be read by the receiver in +exactly the same order in which they were sent. In the situation where a message +suffers a packet loss, this prevents any subsequent messages from achieving +completion status prior to recovery of the preceding message. + +The sending function will HANGUP when the free space in the sending buffer does +not exactly fit the whole message, and it will only RESUME if the free space in +the sending buffer grows up to this size. The call to the sending function also +returns with an error when the size of the message exceeds the total size of the +buffer (this can be modified by the `SRTO_SNDBUF` option). In other words, it is +not designed to send just a part of the message -- either the whole message is sent, or nothing at all. -The receiving function will HANGUP until the whole message is available for reading; -if the message spans multiple UDP packets, then the function RESUMES only when -every single packet from the message has been received, including recovered packets, -if any. When the INORDER flag is set to false and parts of multiple messages are -currently available, the first message that is complete (possibly recovered) is -returned. Otherwise the function does a HANGUP until the next message is complete. -The call to the receiving function is rejected if the buffer size is too small +The receiving function will HANGUP until the whole message is available for reading; +if the message spans multiple UDP packets, then the function RESUMES only when +every single packet from the message has been received, including recovered packets, +if any. When the INORDER flag is set to false and parts of multiple messages are +currently available, the first message that is complete (possibly recovered) is +returned. Otherwise the function does a HANGUP until the next message is complete. +The call to the receiving function is rejected if the buffer size is too small for a single message to fit in it. -Note that you can use any of the sending and receiving functions for sending and -receiving messages, except `sendfile/recvfile`, which are dedicated exclusively -for Buffer API. - -For more information, see [APISocketOptions.md](APISocketOptions.md) +Note that you can use any of the sending and receiving functions for sending and +receiving messages, except `sendfile/recvfile`, which are dedicated exclusively +for Buffer API. +For more information, see [APISocketOptions.md](APISocketOptions.md). [Return to top](#srt-api) - + \ No newline at end of file From 05a803c9e1a3194454c5a234a459a180ce5fc9ad Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 14 Dec 2020 18:05:08 +0100 Subject: [PATCH 008/790] [docs] Removed a note regarding IPv6 usage --- docs/API.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/API.md b/docs/API.md index 8e5a9f3cd..45637e2e6 100644 --- a/docs/API.md +++ b/docs/API.md @@ -79,27 +79,26 @@ srt_close(sock); This closes the socket and frees all its resources. Note that the true life of the socket does not end exactly after this function exits - some details are being -finished in a separate "SRT GC" thread. Still, at least all shared system resources +finished in a separate "SRT GC" thread. Still, at least all shared system resources (such as listener port) should be released after this function exits. ### Important Remarks -1. Please note that the use of SRT with `AF_INET6` has not been fully tested; -use at your own risk. -2. SRT uses the system UDP protocol as an underlying communication layer, and so -it uses also UDP sockets. The underlying communication layer is used only -instrumentally, and SRT manages UDP sockets as its own system resource as it -pleases - so in some cases it may be reasonable for multiple SRT sockets to share +1. SRT uses the system UDP protocol as an underlying communication layer, and so +it uses also UDP sockets. The underlying communication layer is used only +instrumentally, and SRT manages UDP sockets as its own system resource as it +pleases - so in some cases it may be reasonable for multiple SRT sockets to share one UDP socket, or for one SRT socket to use multiple UDP sockets. -3. The term "port" used in SRT is occasionally identical to the term "UDP -port". However SRT offers more flexibility than UDP (or TCP, the more logical -similarity) because it manages ports as its own resources. For example, one port + +2. The term "port" used in SRT is occasionally identical to the term "UDP +port". However SRT offers more flexibility than UDP (or TCP, the more logical +similarity) because it manages ports as its own resources. For example, one port may be shared between various services. ## Binding and connecting -Connections are established using the same philosophy as TCP, using functions -with names and signatures similar to the BSD Socket API. What is new here is +Connections are established using the same philosophy as TCP, using functions +with names and signatures similar to the BSD Socket API. What is new here is the _rendezvous_ mode. ### Synopsis From c507c5b4b2a1a2e7e8c75030401f10dcc4d2aa99 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 15 Dec 2020 14:22:48 +0100 Subject: [PATCH 009/790] [tests] Fixed connection timeout test to not reuse broken socket (#1712) --- test/test_connection_timeout.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index bbd87e145..5d0548029 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -172,20 +172,20 @@ TEST_F(TestConnectionTimeout, Nonblocking) { */ TEST_F(TestConnectionTimeout, BlockingLoop) { - const SRTSOCKET client_sock = srt_create_socket(); - ASSERT_GT(client_sock, 0); // socket_id should be > 0 - - // Set connection timeout to 999 ms to reduce the test execution time. - // Also need to hit a time point between two threads: - // srt_connect will check TTL every second, - // CRcvQueue::worker will wait on a socket for 10 ms. - // Need to have a condition, when srt_connect will process the timeout. - const int connection_timeout_ms = 999; - EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); - const sockaddr* psa = reinterpret_cast(&m_sa); + const int connection_timeout_ms = 999; for (int i = 0; i < 10; ++i) { + const SRTSOCKET client_sock = srt_create_socket(); + ASSERT_GT(client_sock, 0); // socket_id should be > 0 + + // Set connection timeout to 999 ms to reduce the test execution time. + // Also need to hit a time point between two threads: + // srt_connect will check TTL every second, + // CRcvQueue::worker will wait on a socket for 10 ms. + // Need to have a condition, when srt_connect will process the timeout. + EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); + EXPECT_EQ(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR); const int error_code = srt_getlasterror(nullptr); @@ -196,9 +196,9 @@ TEST_F(TestConnectionTimeout, BlockingLoop) << error_code << " " << srt_getlasterror_str() << "\n"; break; } - } - EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); + EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); + } } From 88affe56c6867bb30873eb55d9035f9327751a12 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 17 Dec 2020 13:48:30 +0100 Subject: [PATCH 010/790] [core] Minor refactoring of CheckRunningStability (#1713) --- srtcore/core.cpp | 2 +- srtcore/core.h | 2 +- srtcore/group.cpp | 61 +++++++++++++++++++---------------------------- srtcore/group.h | 5 ++++ 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 692c109e1..c9f775661 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1556,7 +1556,7 @@ void CUDT::open() m_iReXmitCount = 1; m_tsUnstableSince = steady_clock::time_point(); - m_tsTmpActiveTime = steady_clock::time_point(); + m_tsTmpActiveSince = steady_clock::time_point(); m_iPktCount = 0; m_iLightACKCount = 1; diff --git a/srtcore/core.h b/srtcore/core.h index 0b2dd5247..26a28780a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1189,7 +1189,7 @@ class CUDT static const size_t MAX_SID_LENGTH = 512; private: // Timers functions - time_point m_tsTmpActiveTime; // time since temporary activated, or 0 if not temporary activated + time_point m_tsTmpActiveSince; // time since temporary activated, or 0 if not temporary activated time_point m_tsUnstableSince; // time since unexpected ACK delay experienced, or 0 if link seems healthy static const int BECAUSE_NO_REASON = 0, // NO BITS diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 9b3a26a01..13082e44b 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2901,14 +2901,14 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // buffer gets empty so that we can make sure that KEEPALIVE will be the // really last sent for longer time. CUDT& u = w_d->ps->core(); - if (!is_zero(u.m_tsTmpActiveTime)) + if (!is_zero(u.m_tsTmpActiveSince)) { CSndBuffer* b = u.m_pSndBuffer; if (b && b->getCurrBufSize() == 0) { HLOGC(gslog.Debug, log << "grp/sendBackup: FRESH IDLE LINK reached empty buffer - setting permanent and KEEPALIVE"); - u.m_tsTmpActiveTime = steady_clock::time_point(); + u.m_tsTmpActiveSince = steady_clock::time_point(); // Send first immediate keepalive. The link is to be turn to IDLE // now so nothing will be sent to it over time and it will start @@ -2937,13 +2937,13 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point // negative value is relatively easy, while introducing a mutex would only add a // deadlock risk and performance degradation. - bool is_unstable = false; + bool is_stable = true; HLOGC(gslog.Debug, log << "grp/sendBackup: CHECK STABLE: @" << d->id << ": TIMEDIFF {response= " << FormatDuration(currtime - u.m_tsLastRspTime) << " ACK=" << FormatDuration(currtime - u.m_tsLastRspAckTime) << " activation=" - << (!is_zero(u.m_tsTmpActiveTime) ? FormatDuration(currtime - u.m_tsTmpActiveTime) : "PAST") + << (!is_zero(u.m_tsTmpActiveSince) ? FormatDuration(currtime - u.m_tsTmpActiveSince) : "PAST") << " unstable=" << (!is_zero(u.m_tsUnstableSince) ? FormatDuration(currtime - u.m_tsUnstableSince) : "NEVER") << "}"); @@ -2951,13 +2951,10 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point if (currtime > u.m_tsLastRspTime) { // The last response predates the start of this function, look at the difference - steady_clock::duration td_responsive = currtime - u.m_tsLastRspTime; - - IF_HEAVY_LOGGING(string source = "heard"); - + const steady_clock::duration td_responsive = currtime - u.m_tsLastRspTime; bool check_stability = true; - if (!is_zero(u.m_tsTmpActiveTime) && u.m_tsTmpActiveTime < currtime) + if (!is_zero(u.m_tsTmpActiveSince) && u.m_tsTmpActiveSince < currtime) { // The link is temporary-activated. Calculate then since the activation time. @@ -2984,7 +2981,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point // As we DO have activation time, we need to check if there's at least // one ACK newer than activation, that is, td_acked < td_active - if (u.m_tsLastRspAckTime < u.m_tsTmpActiveTime) + if (u.m_tsLastRspAckTime < u.m_tsTmpActiveSince) { check_stability = false; HLOGC(gslog.Debug, @@ -2994,7 +2991,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point } else { - u.m_tsTmpActiveTime = steady_clock::time_point(); + u.m_tsTmpActiveSince = steady_clock::time_point(); } } @@ -3003,7 +3000,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point if (is_zero(u.m_tsUnstableSince)) { HLOGC(gslog.Debug, - log << "grp/sendBackup: socket NEW UNSTABLE: @" << d->id << " last " << source << " " + log << "grp/sendBackup: socket NEW UNSTABLE: @" << d->id << " last heard " << FormatDuration(td_responsive) << " > " << m_uOPT_StabilityTimeout << " (stability timeout)"); // The link seems to have missed two ACKs already. @@ -3012,36 +3009,28 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point u.m_tsUnstableSince = currtime; } - is_unstable = true; + is_stable = false; } } - if (!is_unstable) + if (is_stable) { // If stability is ok, but unstable-since was set before, reset it. HLOGC(gslog.Debug, log << "grp/sendBackup: link STABLE: @" << d->id - << (!is_zero(u.m_tsUnstableSince) ? " - RESTORED" : " - CONTINUED")); + << (!is_zero(u.m_tsUnstableSince) ? " - RESTORED" : " - CONTINUED") + << ", state RUNNING - will send a payload"); u.m_tsUnstableSince = steady_clock::time_point(); - is_unstable = false; } - -#if ENABLE_HEAVY_LOGGING - // Could be set above - if (is_unstable) + else { HLOGC(gslog.Debug, log << "grp/sendBackup: link UNSTABLE for " << FormatDuration(currtime - u.m_tsUnstableSince) << " : @" << d->id << " - will send a payload"); } - else - { - HLOGC(gslog.Debug, log << "grp/sendBackup: socket in RUNNING state: @" << d->id << " - will send a payload"); - } -#endif - return !is_unstable; + return is_stable; } // [[using locked(this->m_GroupLock)]] @@ -3269,7 +3258,7 @@ size_t CUDTGroup::sendBackup_CheckNeedActivate(const vector& idler if (d->sndstate != SRT_GST_RUNNING) { steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsTmpActiveTime = currtime; + d->ps->core().m_tsTmpActiveSince = currtime; HLOGC(gslog.Debug, log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno << " LINK ACTIVATED (pri: " << d->weight << ")."); @@ -3434,7 +3423,7 @@ struct FByOldestActive CUDT& x = a->ps->core(); CUDT& y = b->ps->core(); - return x.m_tsTmpActiveTime < y.m_tsTmpActiveTime; + return x.m_tsTmpActiveSince < y.m_tsTmpActiveSince; } }; @@ -3644,7 +3633,7 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, w_parallel.push_back(d); w_final_stat = stat; steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsTmpActiveTime = currtime; + d->ps->core().m_tsTmpActiveSince = currtime; d->sndstate = SRT_GST_RUNNING; w_none_succeeded = false; HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); @@ -3677,7 +3666,7 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, vector::iterator b = w_parallel.begin(); // Additional criterion: if you have multiple links with the same weight, - // check if you have at least one with m_tsTmpActiveTime == 0. If not, + // check if you have at least one with m_tsTmpActiveSince == 0. If not, // sort them additionally by this time. vector::iterator b1 = b, e = ++b1; @@ -3716,8 +3705,8 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, } CUDT& ce = d->ps->core(); steady_clock::duration td(0); - if (!is_zero(ce.m_tsTmpActiveTime) && - count_microseconds(td = currtime - ce.m_tsTmpActiveTime) < ce.m_uOPT_StabilityTimeout) + if (!is_zero(ce.m_tsTmpActiveSince) && + count_microseconds(td = currtime - ce.m_tsTmpActiveSince) < ce.m_uOPT_StabilityTimeout) { HLOGC(gslog.Debug, log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " @@ -3727,8 +3716,8 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, // Clear activation time because the link is no longer active! d->sndstate = SRT_GST_IDLE; - HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsTmpActiveTime)); - ce.m_tsTmpActiveTime = steady_clock::time_point(); + HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsTmpActiveSince)); + ce.m_tsTmpActiveSince = steady_clock::time_point(); } } } @@ -4370,7 +4359,7 @@ void CUDTGroup::handleKeepalive(gli_t gli) // which may result not only with exceeded stability timeout (which fortunately // isn't being measured in this case), but also with receiveing keepalive // (therefore we also don't reset the link to IDLE in the temporary activation period). - if (gli->sndstate == SRT_GST_RUNNING && is_zero(gli->ps->core().m_tsTmpActiveTime)) + if (gli->sndstate == SRT_GST_RUNNING && is_zero(gli->ps->core().m_tsTmpActiveSince)) { gli->sndstate = SRT_GST_IDLE; HLOGC(gslog.Debug, @@ -4390,7 +4379,7 @@ void CUDTGroup::internalKeepalive(gli_t gli) { gli->rcvstate = SRT_GST_IDLE; // Prevent sending KEEPALIVE again in group-sending - gli->ps->core().m_tsTmpActiveTime = steady_clock::time_point(); + gli->ps->core().m_tsTmpActiveSince = steady_clock::time_point(); HLOGC(gslog.Debug, log << "GROUP: EXP-requested KEEPALIVE in @" << gli->id << " - link turning IDLE"); } } diff --git a/srtcore/group.h b/srtcore/group.h index 75d58eac6..9e226bfd9 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -233,7 +233,12 @@ class CUDTGroup // Support functions for sendBackup and sendBroadcast bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pending); void sendBackup_CheckIdleTime(gli_t w_d); + + /// Check if a running link is stable. + /// @retval true running link is stable + /// @retval false running link is unstable bool sendBackup_CheckRunningStability(const gli_t d, const time_point currtime); + bool sendBackup_CheckSendStatus(const gli_t d, const time_point& currtime, const int stat, From 4b7616a897519f3257554df3f5e797f1b6783019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 15 Dec 2020 12:36:28 +0100 Subject: [PATCH 011/790] [docs] Updated outdated API info --- docs/API.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/API.md b/docs/API.md index 45637e2e6..328faa463 100644 --- a/docs/API.md +++ b/docs/API.md @@ -55,20 +55,17 @@ it is used to define a point of communication. ### Synopsis ```c++ -SRTSOCKET srt_socket(int af, int, int); +SRTSOCKET srt_create_socket(); int srt_close(SRTSOCKET s); ``` -The `srt_socket` function is based on the legacy UDT API except -the first parameter. The other two are ignored. - Note that `SRTSOCKET` is just an alias for `int`; this is a legacy naming convention from UDT, which is here only for clarity. ### Usage ```c++ -sock = srt_socket(AF_INET, SOCK_DGRAM, 0); +sock = srt_create_socket(); ``` This creates a socket, which can next be configured and then used for communication. @@ -105,7 +102,7 @@ the _rendezvous_ mode. ```c++ int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); -int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock); +int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); ``` This function sets up the "sockname" for the socket, that is, the local IP address @@ -114,8 +111,8 @@ this can be done on both listening and connecting sockets; for the latter it wil define the outgoing port. If you don't set up the outgoing port by calling this function (or use port number 0), a unique port number will be selected automatically. -The `*_peerof` version simply copies the bound address setting from an existing -UDP socket. +The `*_acquire` version simply takes over the given UDP socket and copies the +bound address setting from it. ```c++ int srt_listen(SRTSOCKET u, int backlog); @@ -705,4 +702,4 @@ for Buffer API. For more information, see [APISocketOptions.md](APISocketOptions.md). [Return to top](#srt-api) - \ No newline at end of file + From ba883c336ab986cc42ab0d615565c8af5b96e24d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 30 Nov 2020 15:12:19 +0100 Subject: [PATCH 012/790] [core] CSync::wait_until is now mapped to CV::wait_until instead of wait_for. When C++11 sync is enabled, it will reduce the number of time conversions. --- srtcore/core.cpp | 2 +- srtcore/sync.h | 20 +++++++------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c9f775661..714b8a163 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5773,7 +5773,7 @@ void *CUDT::tsbpd(void *param) log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << current_pkt_seq << " T=" << FormatTime(tsbpdtime) << " - waiting " << count_milliseconds(timediff) << "ms"); THREAD_PAUSED(); - tsbpd_cc.wait_for(timediff); + tsbpd_cc.wait_until(tsbpdtime); THREAD_RESUMED(); } else diff --git a/srtcore/sync.h b/srtcore/sync.h index 6bda44fae..de739380c 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -475,26 +475,20 @@ class CSync /// Block the call until either @a timestamp time achieved /// or the conditional is signaled. /// @param [in] delay Maximum time to wait since the moment of the call - /// @retval true Resumed due to getting a CV signal - /// @retval false Resumed due to being past @a timestamp + /// @retval false if the relative timeout specified by rel_time expired, + /// @retval true if condition is signaled or spurious wake up. bool wait_for(const steady_clock::duration& delay) { return m_cond->wait_for(*m_locker, delay); } - // Wait until the given time is achieved. This actually - // refers to wait_for for the time remaining to achieve - // given time. + // Wait until the given time is achieved. + /// @param [in] exptime The target time to wait until. + /// @retval false if the target wait time is reached. + /// @retval true if condition is signal or spurious wake up. bool wait_until(const steady_clock::time_point& exptime) { - // This will work regardless as to which clock is in use. The time - // should be specified as steady_clock::time_point, so there's no - // question of the timer base. - steady_clock::time_point now = steady_clock::now(); - if (now >= exptime) - return false; // timeout - - return wait_for(exptime - now); + return m_cond->wait_until(*m_locker, exptime); } // Static ad-hoc version From c12e6198b0cbf6dc8759fe87f533e15111f245dc Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 18 Dec 2020 10:03:39 +0100 Subject: [PATCH 013/790] [core] Refactored member pointer: now raw pointer to socket data (#1696) --- srtcore/api.cpp | 102 +++++++++++++++--------------- srtcore/api.h | 11 ++-- srtcore/core.cpp | 154 +++++++++++++++++++++++----------------------- srtcore/group.cpp | 58 ++++++++--------- srtcore/group.h | 28 ++++----- 5 files changed, 175 insertions(+), 178 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 2e9c4014b..5d0aa2904 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -86,8 +86,8 @@ extern LogConfig srt_logger_config; void CUDTSocket::construct() { #if ENABLE_EXPERIMENTAL_BONDING - m_IncludedGroup = NULL; - m_IncludedIter = CUDTGroup::gli_NULL(); + m_GroupOf = NULL; + m_GroupMemberData = NULL; #endif setupMutex(m_AcceptLock, "Accept"); setupCond(m_AcceptCond, "Accept"); @@ -677,16 +677,16 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, error = 2; } - // The access to m_IncludedGroup should be also protected, as the group + // The access to m_GroupOf should be also protected, as the group // could be requested deletion in the meantime. This will hold any possible - // removal from group and resetting m_IncludedGroup field. + // removal from group and resetting m_GroupOf field. #if ENABLE_EXPERIMENTAL_BONDING - if (ns->m_IncludedGroup) + if (ns->m_GroupOf) { // XXX this might require another check of group type. // For redundancy group, at least, update the status in the group - CUDTGroup* g = ns->m_IncludedGroup; + CUDTGroup* g = ns->m_GroupOf; ScopedLock glock (g->m_GroupLock); if (g->m_bClosing) { @@ -694,13 +694,11 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, goto ERR_ROLLBACK; } - CUDTGroup::gli_t gi; - // Check if this is the first socket in the group. // If so, give it up to accept, otherwise just do nothing // The client will be informed about the newly added connection at the // first moment when attempting to get the group status. - for (gi = g->m_Group.begin(); gi != g->m_Group.end(); ++gi) + for (CUDTGroup::gli_t gi = g->m_Group.begin(); gi != g->m_Group.end(); ++gi) { if (gi->laststatus == SRTS_CONNECTED) { @@ -713,13 +711,13 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, // Update the status in the group so that the next // operation can include the socket in the group operation. - gi = ns->m_IncludedIter; + CUDTGroup::SocketData* gm = ns->m_GroupMemberData; HLOGC(cnlog.Debug, log << "newConnection(GROUP): Socket @" << ns->m_SocketID << " BELONGS TO $" << g->id() << " - will " << (should_submit_to_accept? "" : "NOT ") << "report in accept"); - gi->sndstate = SRT_GST_IDLE; - gi->rcvstate = SRT_GST_IDLE; - gi->laststatus = SRTS_CONNECTED; + gm->sndstate = SRT_GST_IDLE; + gm->rcvstate = SRT_GST_IDLE; + gm->laststatus = SRTS_CONNECTED; if (!g->m_bConnected) { @@ -845,9 +843,9 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, ScopedLock cg(m_GlobControlLock); #if ENABLE_EXPERIMENTAL_BONDING - if (ns->m_IncludedGroup) + if (ns->m_GroupOf) { - HLOGC(smlog.Debug, log << "@" << ns->m_SocketID << " IS MEMBER OF $" << ns->m_IncludedGroup->id() << " - REMOVING FROM GROUP"); + HLOGC(smlog.Debug, log << "@" << ns->m_SocketID << " IS MEMBER OF $" << ns->m_GroupOf->id() << " - REMOVING FROM GROUP"); ns->removeFromGroup(true); } #endif @@ -1178,16 +1176,16 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ // and the already accepted socket has successfully joined // the mirror group. If so, RETURN THE GROUP ID, not the socket ID. #if ENABLE_EXPERIMENTAL_BONDING - if (ls->m_pUDT->m_OPT_GroupConnect == 1 && s->m_IncludedGroup) + if (ls->m_pUDT->m_OPT_GroupConnect == 1 && s->m_GroupOf) { // Put a lock to protect the group against accidental deletion // in the meantime. ScopedLock glock (m_GlobControlLock); // Check again; it's unlikely to happen, but // it's a theoretically possible scenario - if (s->m_IncludedGroup) + if (s->m_GroupOf) { - u = s->m_IncludedGroup->m_GroupID; + u = s->m_GroupOf->m_GroupID; s->core().m_OPT_GroupConnect = 1; // should be derived from ls, but make sure } else @@ -1495,9 +1493,9 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar if (proceed) { - CUDTGroup::gli_t f = g.add(data); - ns->m_IncludedIter = f; - ns->m_IncludedGroup = &g; + CUDTGroup::SocketData* f = g.add(data); + ns->m_GroupMemberData = f; + ns->m_GroupOf = &g; f->weight = targets[tii].weight; LOGC(aclog.Note, log << "srt_connect_group: socket @" << sid << " added to group $" << g.m_GroupID); } @@ -1608,7 +1606,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar // set busy, so it won't be deleted, even if it was requested to be closed. ScopedLock grd (g.m_GroupLock); - if (!ns->m_IncludedGroup) + if (!ns->m_GroupOf) { // The situation could get changed between the unlock and lock of m_GroupLock. // This must be checked again. @@ -1621,8 +1619,8 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar continue; } - // If m_IncludedGroup is not NULL, the m_IncludedIter is still valid. - CUDTGroup::gli_t f = ns->m_IncludedIter; + // If m_GroupOf is not NULL, the m_IncludedIter is still valid. + CUDTGroup::SocketData* f = ns->m_GroupMemberData; // Now under a group lock, we need to make sure the group isn't being closed // in order not to add a socket to a dead group. @@ -1957,11 +1955,11 @@ void CUDTUnited::deleteGroup(CUDTGroup* g) i != m_Sockets.end(); ++ i) { CUDTSocket* s = i->second; - if (s->m_IncludedGroup == g) + if (s->m_GroupOf == g) { HLOGC(smlog.Debug, log << "deleteGroup: IPE: existing @" << s->m_SocketID << " points to a dead group!"); - s->m_IncludedGroup = NULL; - s->m_IncludedIter = CUDTGroup::gli_NULL(); + s->m_GroupOf = NULL; + s->m_GroupMemberData = NULL; } } @@ -1971,11 +1969,11 @@ void CUDTUnited::deleteGroup(CUDTGroup* g) i != m_ClosedSockets.end(); ++ i) { CUDTSocket* s = i->second; - if (s->m_IncludedGroup == g) + if (s->m_GroupOf == g) { HLOGC(smlog.Debug, log << "deleteGroup: IPE: closed @" << s->m_SocketID << " points to a dead group!"); - s->m_IncludedGroup = NULL; - s->m_IncludedIter = CUDTGroup::gli_NULL(); + s->m_GroupOf = NULL; + s->m_GroupMemberData = NULL; } } } @@ -2048,9 +2046,9 @@ int CUDTUnited::close(CUDTSocket* s) s->setClosed(); #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_IncludedGroup) + if (s->m_GroupOf) { - HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_IncludedGroup->id() << " - REMOVING FROM GROUP"); + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); s->removeFromGroup(true); } #endif @@ -2563,7 +2561,7 @@ CUDTGroup* CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) CUDTGroup* CUDTUnited::acquireSocketsGroup(CUDTSocket* s) { ScopedLock cg (m_GlobControlLock); - CUDTGroup* g = s->m_IncludedGroup; + CUDTGroup* g = s->m_GroupOf; if (!g) return NULL; @@ -2672,9 +2670,9 @@ void CUDTUnited::checkBrokenSockets() } #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_IncludedGroup) + if (s->m_GroupOf) { - LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_IncludedGroup->id() << " - REMOVING FROM GROUP"); + LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); s->removeFromGroup(true); } #endif @@ -2757,9 +2755,9 @@ void CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* const s = i->second; #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_IncludedGroup) + if (s->m_GroupOf) { - HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_IncludedGroup->id() << " - REMOVING FROM GROUP"); + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); s->removeFromGroup(true); } #endif @@ -3101,9 +3099,9 @@ void* CUDTUnited::garbageCollect(void* p) s->breakSocket_LOCKED(); #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_IncludedGroup) + if (s->m_GroupOf) { - HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_IncludedGroup->id() << " (IPE?) - REMOVING FROM GROUP"); + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " (IPE?) - REMOVING FROM GROUP"); s->removeFromGroup(false); } #endif @@ -3262,7 +3260,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Check if the socket is already IN SOME GROUP. - if (s->m_IncludedGroup) + if (s->m_GroupOf) return APIError(MJ_NOTSUP, MN_INVAL, 0); CUDTGroup* g = k.group; @@ -3282,17 +3280,17 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Check if the socket already is in the group - CUDTGroup::gli_t f = g->find(socket); - if (f != CUDTGroup::gli_NULL()) + CUDTGroup::SocketData* f; + if (g->contains(socket, (f))) { // XXX This is internal error. Report it, but continue LOGC(aclog.Error, log << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); - s->m_IncludedIter = f; - s->m_IncludedGroup = g; + s->m_GroupMemberData = f; + s->m_GroupOf = g; return 0; } - s->m_IncludedIter = g->add(g->prepareData(s)); - s->m_IncludedGroup = g; + s->m_GroupMemberData = g->add(g->prepareData(s)); + s->m_GroupOf = g; return 0; } @@ -3305,7 +3303,7 @@ int CUDT::removeSocketFromGroup(SRTSOCKET socket) if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); - if (!s->m_IncludedGroup) + if (!s->m_GroupOf) return APIError(MJ_NOTSUP, MN_INVAL, 0); ScopedLock cg (s->m_ControlLock); @@ -3318,7 +3316,7 @@ int CUDT::removeSocketFromGroup(SRTSOCKET socket) // [[using locked(CUDT::s_UDTUnited.m_GlobControlLock)]] void CUDTSocket::removeFromGroup(bool broken) { - CUDTGroup* g = m_IncludedGroup; + CUDTGroup* g = m_GroupOf; if (g) { // Reset group-related fields immediately. They won't be accessed @@ -3326,8 +3324,8 @@ void CUDTSocket::removeFromGroup(bool broken) // a short moment between removal from the group container and the end, // while the GroupLock would be already taken out. It is safer to reset // it to a NULL iterator before removal. - m_IncludedGroup = NULL; - m_IncludedIter = CUDTGroup::gli_NULL(); + m_GroupOf = NULL; + m_GroupMemberData = NULL; bool still_have = g->remove(m_SocketID); if (broken) @@ -3351,10 +3349,10 @@ SRTSOCKET CUDT::getGroupOfSocket(SRTSOCKET socket) // to persist the call. ScopedLock glock (s_UDTUnited.m_GlobControlLock); CUDTSocket* s = s_UDTUnited.locateSocket_LOCKED(socket); - if (!s || !s->m_IncludedGroup) + if (!s || !s->m_GroupOf) return APIError(MJ_NOTSUP, MN_INVAL, 0); - return s->m_IncludedGroup->id(); + return s->m_GroupOf->id(); } int CUDT::configureGroup(SRTSOCKET groupid, const char* str) diff --git a/srtcore/api.h b/srtcore/api.h index 8ad6bb3fd..fa18a6bba 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -83,7 +83,8 @@ class CUDTSocket , m_ListenSocket(0) , m_PeerID(0) #if ENABLE_EXPERIMENTAL_BONDING - , m_IncludedGroup() + , m_GroupMemberData() + , m_GroupOf() #endif , m_iISN(0) , m_pUDT(NULL) @@ -110,16 +111,16 @@ class CUDTSocket /// 1 second (see CUDTUnited::checkBrokenSockets()). srt::sync::steady_clock::time_point m_tsClosureTimeStamp; - sockaddr_any m_SelfAddr; //< local address of the socket - sockaddr_any m_PeerAddr; //< peer address of the socket + sockaddr_any m_SelfAddr; //< local address of the socket + sockaddr_any m_PeerAddr; //< peer address of the socket SRTSOCKET m_SocketID; //< socket ID SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket SRTSOCKET m_PeerID; //< peer socket ID #if ENABLE_EXPERIMENTAL_BONDING - CUDTGroup::gli_t m_IncludedIter; //< Container's iterator of the group to which it belongs, or gli_NULL() if it isn't - CUDTGroup* m_IncludedGroup; //< Group this socket is a member of, or NULL if it isn't + CUDTGroup::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member + CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't #endif int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 714b8a163..38a492e6e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1882,17 +1882,17 @@ size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& st // [[using locked(s_UDTUnited.m_GlobControlLock)]] size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) { - SRT_ASSERT(m_parent->m_IncludedGroup != NULL); + SRT_ASSERT(m_parent->m_GroupOf != NULL); uint32_t* space = pcmdspec + 1; - SRTSOCKET id = m_parent->m_IncludedGroup->id(); - SRT_GROUP_TYPE tp = m_parent->m_IncludedGroup->type(); + SRTSOCKET id = m_parent->m_GroupOf->id(); + SRT_GROUP_TYPE tp = m_parent->m_GroupOf->type(); uint32_t flags = 0; // Note: if agent is a listener, and the current version supports // both sync methods, this flag might have been changed according to // the wish of the caller. - if (m_parent->m_IncludedGroup->synconmsgno()) + if (m_parent->m_GroupOf->synconmsgno()) flags |= SRT_GFLAG_SYNCONMSG; // NOTE: this code remains as is for historical reasons. @@ -1902,7 +1902,7 @@ size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) // extension, but it was later seen not necessary. Therefore // this code remains, but now it's informational only. #if ENABLE_HEAVY_LOGGING - m_parent->m_IncludedGroup->debugMasterData(m_SocketID); + m_parent->m_GroupOf->debugMasterData(m_SocketID); #endif // See CUDT::interpretGroup() @@ -1910,7 +1910,7 @@ size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) uint32_t dataword = 0 | SrtHSRequest::HS_GROUP_TYPE::wrap(tp) | SrtHSRequest::HS_GROUP_FLAGS::wrap(flags) - | SrtHSRequest::HS_GROUP_WEIGHT::wrap(m_parent->m_IncludedIter->weight); + | SrtHSRequest::HS_GROUP_WEIGHT::wrap(m_parent->m_GroupMemberData->weight); const uint32_t storedata [GRPD_E_SIZE] = { uint32_t(id), dataword }; memcpy((space), storedata, sizeof storedata); @@ -2193,7 +2193,7 @@ bool CUDT::createSrtHandshake( // hurt, even if this field could be dangling in the moment. This will be // followed by an additional check, done this time under lock, and there will // be no dangling pointers at this time. - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // Whatever group this socket belongs to, the information about // the group is always sent the same way with the handshake. @@ -2303,9 +2303,9 @@ bool CUDT::createSrtHandshake( // NOTE: See information about mutex ordering in api.h ScopedLock grd (m_parent->m_ControlLock); // Required to make sure ScopedLock gdrg (s_UDTUnited.m_GlobControlLock); - if (!m_parent->m_IncludedGroup) + if (!m_parent->m_GroupOf) { - // This may only happen if since last check of m_IncludedGroup pointer the socket was removed + // This may only happen if since last check of m_GroupOf pointer the socket was removed // from the group in the meantime, which can only happen due to that the group was closed. // In such a case it simply means that the handshake process was requested to be interrupted. LOGC(cnlog.Fatal, log << "GROUP DISAPPEARED. Socket not capable of continuing HS"); @@ -2313,7 +2313,7 @@ bool CUDT::createSrtHandshake( } else { - if (m_parent->m_IncludedGroup->closing()) + if (m_parent->m_GroupOf->closing()) { m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Error, log << "createSrtHandshake: group is closing during the process, rejecting."); @@ -3390,9 +3390,9 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, } #if ENABLE_EXPERIMENTAL_BONDING - // m_IncludedGroup and locking info: NULL check won't hurt here. If the group + // m_GroupOf and locking info: NULL check won't hurt here. If the group // was deleted in the meantime, it will be found out later anyway and result with error. - if (m_SrtHsSide == HSD_INITIATOR && m_parent->m_IncludedGroup) + if (m_SrtHsSide == HSD_INITIATOR && m_parent->m_GroupOf) { // XXX Later probably needs to check if this group REQUIRES the group // response. Currently this implements the bonding-category group, and this @@ -3567,7 +3567,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN // the same id, otherwise the connection should be rejected. // So, first check the group of the current socket and see if a peer is set. - CUDTGroup* pg = m_parent->m_IncludedGroup; + CUDTGroup* pg = m_parent->m_GroupOf; if (!pg) { // This means that the responder has responded with a group membership, @@ -3630,7 +3630,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; // error occurred } - if (!m_parent->m_IncludedGroup) + if (!m_parent->m_GroupOf) { // Strange, we just added it... m_RejectReason = SRT_REJ_IPE; @@ -3638,14 +3638,14 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } - CUDTGroup::gli_t f = m_parent->m_IncludedIter; + CUDTGroup::SocketData* f = m_parent->m_GroupMemberData; f->weight = link_weight; f->agent = m_parent->m_SelfAddr; f->peer = m_PeerAddr; } - m_parent->m_IncludedGroup->debugGroup(); + m_parent->m_GroupOf->debugGroup(); // That's all. For specific things concerning group // types, this will be later. @@ -3737,19 +3737,19 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l // Copy of addSocketToGroup. No idea how many parts could be common, not much. // Check if the socket already is in the group - CUDTGroup::gli_t f = gp->find(m_SocketID); - if (f != CUDTGroup::gli_NULL()) + CUDTGroup::SocketData* f; + if (gp->contains(m_SocketID, (f))) { // XXX This is internal error. Report it, but continue // (A newly created socket from acceptAndRespond should not have any group membership yet) LOGC(gmlog.Error, log << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); - s->m_IncludedGroup = gp; - s->m_IncludedIter = f; + s->m_GroupOf = gp; + s->m_GroupMemberData = f; return 0; } - s->m_IncludedIter = gp->add(gp->prepareData(s)); - s->m_IncludedGroup = gp; + s->m_GroupMemberData = gp->add(gp->prepareData(s)); + s->m_GroupOf = gp; // Record the remote address in the group data. @@ -5032,7 +5032,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE { #if ENABLE_EXPERIMENTAL_BONDING ScopedLock cl (s_UDTUnited.m_GlobControlLock); - CUDTGroup* g = m_parent->m_IncludedGroup; + CUDTGroup* g = m_parent->m_GroupOf; if (g) { // This is the last moment when this can be done. @@ -5133,7 +5133,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE #if ENABLE_EXPERIMENTAL_BONDING { ScopedLock cl (s_UDTUnited.m_GlobControlLock); - CUDTGroup* g = m_parent->m_IncludedGroup; + CUDTGroup* g = m_parent->m_GroupOf; if (g) { // XXX this might require another check of group type. @@ -5142,13 +5142,13 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE // LEAVING as comment for historical reasons. Locking is here most // likely not necessary because the socket cannot be removed from the // group until the socket isn't removed, and this requires locking of - // m_GlobControlLock. This should ensure that when m_IncludedGroup is - // not NULL, m_IncludedIter is also valid. + // m_GlobControlLock. This should ensure that when m_GroupOf is + // not NULL, m_GroupMemberData is also valid. // ScopedLock glock(g->m_GroupLock); HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE"); - CUDTGroup::gli_t gi = m_parent->m_IncludedIter; + CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; gi->sndstate = SRT_GST_IDLE; gi->rcvstate = SRT_GST_IDLE; gi->laststatus = SRTS_CONNECTED; @@ -5743,7 +5743,7 @@ void *CUDT::tsbpd(void *param) // the next CUDTGroup::recv() call should return with no blocking or not. // When the group is read-ready, it should update its pollers as it sees fit. - // NOTE: this call will set lock to m_IncludedGroup->m_GroupLock + // NOTE: this call will set lock to m_GroupOf->m_GroupLock HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: GROUP: checking if %" << current_pkt_seq << " makes group readable"); gkeeper.group->updateReadState(self->m_SocketID, current_pkt_seq); @@ -5986,7 +5986,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, { #if ENABLE_EXPERIMENTAL_BONDING ScopedLock cl (s_UDTUnited.m_GlobControlLock); - CUDTGroup* g = m_parent->m_IncludedGroup; + CUDTGroup* g = m_parent->m_GroupOf; if (g) { // This is the last moment when this can be done. @@ -6558,7 +6558,7 @@ int CUDT::receiveBuffer(char *data, int len) return res; } -// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_IncludedGroup != NULL)]]; +// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; void CUDT::checkNeedDrop(bool& w_bCongestion) { @@ -6638,9 +6638,9 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) // What's important is that the lock on GroupLock cannot be applied // here, both because it might be applied already, and because the // locks on the later lock ordered mutexes are already set. - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { - m_parent->m_IncludedGroup->ackMessage(first_msgno); + m_parent->m_GroupOf->ackMessage(first_msgno); } #endif } @@ -6665,9 +6665,9 @@ int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t sr return this->sendmsg2(data, len, (mctrl)); } -// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_IncludedGroup != NULL)]] +// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::send, -// which is the only case when the m_parent->m_IncludedGroup is not NULL. +// which is the only case when the m_parent->m_GroupOf is not NULL. int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { bool bCongestion = false; @@ -6959,16 +6959,16 @@ int CUDT::recvmsg(char* data, int len, int64_t& srctime) return res; } -// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_IncludedGroup != NULL)]] +// [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::recv, -// which is the only case when the m_parent->m_IncludedGroup is not NULL. +// which is the only case when the m_parent->m_GroupOf is not NULL. int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) { // Check if the socket is a member of a receiver group. // If so, then reading by receiveMessage is disallowed. #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_IncludedGroup && m_parent->m_IncludedGroup->isGroupReceiver()) + if (m_parent->m_GroupOf && m_parent->m_GroupOf->isGroupReceiver()) { LOGP(arlog.Error, "recv*: This socket is a receiver group member. Use group ID, NOT socket ID."); throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI, 0); @@ -8139,7 +8139,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // required in the defined order. At present we only need the lock // on m_GlobControlLock to prevent the group from being deleted // in the meantime - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // Check is first done before locking to avoid unnecessary // mutex locking. The condition for this field is that it @@ -8148,14 +8148,14 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // the dangling case. ScopedLock glock (s_UDTUnited.m_GlobControlLock); - // Note that updateLatestRcv will lock m_IncludedGroup->m_GroupLock, + // Note that updateLatestRcv will lock m_GroupOf->m_GroupLock, // but this is an intended order. - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // A group may need to update the parallelly used idle links, // should it have any. Pass the current socket position in order // to skip it from the group loop. - m_parent->m_IncludedGroup->updateLatestRcv(m_parent); + m_parent->m_GroupOf->updateLatestRcv(m_parent); } } #endif @@ -8187,17 +8187,17 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // acknowledge any waiting epolls to read s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // See above explanation for double-checking ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // The current "APP reader" needs to simply decide as to whether // the next CUDTGroup::recv() call should return with no blocking or not. // When the group is read-ready, it should update its pollers as it sees fit. - m_parent->m_IncludedGroup->updateReadState(m_SocketID, first_seq); + m_parent->m_GroupOf->updateReadState(m_SocketID, first_seq); } } #endif @@ -8304,7 +8304,7 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // This is for the call of CSndBuffer::getMsgNoAt that returns // this value as a notfound-trap. int32_t msgno_at_last_acked_seq = SRT_MSGNO_CONTROL; - bool is_group = m_parent->m_IncludedGroup; + bool is_group = m_parent->m_GroupOf; #endif // Update sender's loss list and acknowledge packets in the sender's buffer @@ -8350,12 +8350,12 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // m_RecvAckLock is ordered AFTER m_GlobControlLock, so this can only // be done now that m_RecvAckLock is unlocked. ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { HLOGC(xtlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); // NOTE: ackMessage also accepts and ignores the trap representation // which is SRT_MSGNO_CONTROL. - m_parent->m_IncludedGroup->ackMessage(msgno_at_last_acked_seq); + m_parent->m_GroupOf->ackMessage(msgno_at_last_acked_seq); } } #endif @@ -8488,14 +8488,14 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point // leaveCS(m_RecvAckLock); #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // Will apply m_GroupLock, ordered after m_GlobControlLock. // m_GlobControlLock is necessary for group existence. - m_parent->m_IncludedGroup->updateWriteState(); + m_parent->m_GroupOf->updateWriteState(); } } #endif @@ -8782,12 +8782,12 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING - if (drift_updated && m_parent->m_IncludedGroup) + if (drift_updated && m_parent->m_GroupOf) { ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { - m_parent->m_IncludedGroup->synchronizeDrift(this, udrift, newtimebase); + m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); } } #endif @@ -9107,11 +9107,11 @@ void CUDT::updateAfterSrtHandshake(int hsv) #if ENABLE_EXPERIMENTAL_BONDING string grpspec; - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { ScopedLock glock (s_UDTUnited.m_GlobControlLock); - grpspec = m_parent->m_IncludedGroup - ? " group=$" + Sprint(m_parent->m_IncludedGroup->id()) + grpspec = m_parent->m_GroupOf + ? " group=$" + Sprint(m_parent->m_GroupOf->id()) : string(); } #else @@ -9330,7 +9330,7 @@ std::pair CUDT::packData(CPacket& w_packet) // sequence is moved due to the difference between ISN caught during the existing // transmission and the first sequence possible to be used at the first sending // instruction. The group itself isn't being accessed. - if (m_parent->m_IncludedGroup && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) + if (m_parent->m_GroupOf && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) { const int packetspan = CSeqNo::seqcmp(w_packet.m_iSeqNo, m_iSndCurrSeqNo); @@ -9371,7 +9371,7 @@ std::pair CUDT::packData(CPacket& w_packet) << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); #if ENABLE_EXPERIMENTAL_BONDING - HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_IncludedGroup ? "yes":"no") + HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_GroupOf ? "yes":"no") << " extraction-seq=" << m_iSndCurrSeqNo << " scheduling-seq=" << w_packet.m_iSeqNo << " ISN=" << m_iISN); #endif @@ -9823,16 +9823,16 @@ int CUDT::processData(CUnit* in_unit) // accepted or rejected because if it was belated it may result in a // "runaway train" problem as the IDLE links are being updated the base // reception sequence pointer stating that this link is not receiving. - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); - CUDTGroup::gli_t gi = m_parent->m_IncludedIter; + CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long // as gi is non-NULL iterator, the group does exist and it does contain // this socket as member (that is, 'gi' cannot be a dangling iterator). - if (gi != CUDTGroup::gli_NULL()) + if (gi != NULL) { if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { @@ -10323,9 +10323,9 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) // XXX When an alternative packet arrival callback is installed // in case of groups, move this part to the groupwise version. - if (self->m_parent->m_IncludedGroup) + if (self->m_parent->m_GroupOf) { - CUDTGroup::gli_t gi = self->m_parent->m_IncludedIter; + CUDTGroup::SocketData* gi = self->m_parent->m_GroupMemberData; if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); @@ -11257,15 +11257,15 @@ void CUDT::checkTimers() { sendCtrl(UMSG_KEEPALIVE); #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // Pass socket ID because it's about changing group socket data - m_parent->m_IncludedGroup->internalKeepalive(m_parent->m_IncludedIter); + m_parent->m_GroupOf->internalKeepalive(m_parent->m_GroupMemberData); // NOTE: GroupLock is unnecessary here because the only data read and - // modified is the target of the iterator from m_IncludedIter. The + // modified is the target of the iterator from m_GroupMemberData. The // iterator will be valid regardless of any container modifications. } } @@ -11290,21 +11290,21 @@ void CUDT::completeBrokenConnectionDependencies(int errorcode) bool pending_broken = false; { ScopedLock guard_group_existence (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { - token = m_parent->m_IncludedIter->token; - if (m_parent->m_IncludedIter->sndstate == SRT_GST_PENDING) + token = m_parent->m_GroupMemberData->token; + if (m_parent->m_GroupMemberData->sndstate == SRT_GST_PENDING) { HLOGC(gmlog.Debug, log << "updateBrokenConnection: a pending link was broken - will be removed"); pending_broken = true; } else { - HLOGC(gmlog.Debug, log << "updateBrokenConnection: state=" << CUDTGroup::StateStr(m_parent->m_IncludedIter->sndstate) << " a used link was broken - not closing automatically"); + HLOGC(gmlog.Debug, log << "updateBrokenConnection: state=" << CUDTGroup::StateStr(m_parent->m_GroupMemberData->sndstate) << " a used link was broken - not closing automatically"); } - m_parent->m_IncludedIter->sndstate = SRT_GST_BROKEN; - m_parent->m_IncludedIter->rcvstate = SRT_GST_BROKEN; + m_parent->m_GroupMemberData->sndstate = SRT_GST_BROKEN; + m_parent->m_GroupMemberData->rcvstate = SRT_GST_BROKEN; } } #endif @@ -11322,7 +11322,7 @@ void CUDT::completeBrokenConnectionDependencies(int errorcode) // the operation. The attempt of group deletion will // have to wait until this operation completes. ScopedLock lock(s_UDTUnited.m_GlobControlLock); - CUDTGroup* pg = m_parent->m_IncludedGroup; + CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { // Bound to one call because this requires locking @@ -11564,7 +11564,7 @@ void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) // for extra data sent through keepalive. #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_IncludedGroup) + if (m_parent->m_GroupOf) { // Lock GlobControlLock in order to make sure that // the state if the socket having the group and the @@ -11572,13 +11572,13 @@ void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) // the operation. The attempt of group deletion will // have to wait until this operation completes. ScopedLock lock(s_UDTUnited.m_GlobControlLock); - CUDTGroup* pg = m_parent->m_IncludedGroup; + CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { // Whether anything is to be done with this socket // about the fact that keepalive arrived, let the // group handle it - pg->handleKeepalive(m_parent->m_IncludedIter); + pg->handleKeepalive(m_parent->m_GroupMemberData); } } #endif diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 13082e44b..fd5b480ff 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -211,9 +211,7 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // GROUP -std::list CUDTGroup::GroupContainer::s_NoList; - -CUDTGroup::gli_t CUDTGroup::add(SocketData data) +CUDTGroup::SocketData* CUDTGroup::add(SocketData data) { ScopedLock g(m_GroupLock); @@ -242,7 +240,8 @@ CUDTGroup::gli_t CUDTGroup::add(SocketData data) m_iMaxPayloadSize = plsize; } - return --end; + --end; + return &*end; } CUDTGroup::SocketData CUDTGroup::prepareData(CUDTSocket* s) @@ -363,6 +362,7 @@ void CUDTGroup::GroupContainer::erase(CUDTGroup::gli_t it) if (m_List.empty()) { LOGC(gmlog.Error, log << "IPE: GroupContainer is empty and 'erase' is called on it."); + m_LastActiveLink = m_List.end(); return; // this avoids any misunderstandings in iterator checks } @@ -371,7 +371,7 @@ void CUDTGroup::GroupContainer::erase(CUDTGroup::gli_t it) if (bb == m_List.end()) // means: m_List.size() == 1 { // One element, this one being deleted, nothing to point to. - m_LastActiveLink = null(); + m_LastActiveLink = m_List.end(); } else { @@ -971,8 +971,8 @@ void CUDTGroup::close() HLOGC(smlog.Debug, log << "group/close: IPE(NF): group member @" << ig->id << " already deleted"); continue; } - s->m_IncludedGroup = NULL; - s->m_IncludedIter = gli_NULL(); + s->m_GroupOf = NULL; + s->m_GroupMemberData = NULL; HLOGC(smlog.Debug, log << "group/close: CUTTING OFF @" << ig->id << " (found as @" << s->m_SocketID << ") from the group"); } @@ -1257,7 +1257,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) curseq = w_mc.pktseq; } - const Sendstate cstate = {d->id, d, stat, erc}; + const Sendstate cstate = {d->id, &*d, stat, erc}; sendstates.push_back(cstate); d->sndresult = stat; d->laststatus = d->ps->getStatus(); @@ -1307,7 +1307,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) for (vector::iterator i = idlers.begin(); i != idlers.end(); ++i) { gli_t d = *i; - if (!d->ps->m_IncludedGroup) + if (!d->ps->m_GroupOf) continue; int erc = 0; @@ -1355,7 +1355,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) d->sndresult = stat; d->laststatus = d->ps->getStatus(); - const Sendstate cstate = {d->id, d, stat, erc}; + const Sendstate cstate = {d->id, &*d, stat, erc}; sendstates.push_back(cstate); } @@ -1452,7 +1452,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // If there were only some reactivated idlers successful, the first // idler has defined the sequence. - vector successful, blocked; + vector successful, blocked; // This iteration of the state will simply // qualify the remaining sockets into three categories: @@ -1464,7 +1464,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Now - sendstates contain directly sockets. // In order to update members, you need to have locked: // - GlobControlLock to prevent sockets from disappearing or being closed - // - then GroupLock to latch the validity of m_IncludedIter field. + // - then GroupLock to latch the validity of m_GroupMemberData field. { { @@ -1486,11 +1486,11 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Is the socket still group member? If not, SKIP IT. It could only be taken ownership // by being explicitly closed and so it's deleted from the container. - if (!ps->m_IncludedGroup) + if (!ps->m_GroupOf) continue; - // Now we are certain that m_IncludedIter is valid. - gli_t d = ps->m_IncludedIter; + // Now we are certain that m_GroupMemberData is valid. + SocketData* d = ps->m_GroupMemberData; if (is->stat == len) { @@ -1542,7 +1542,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Good. All blocked links are now qualified as broken. // You had your chance, but I can't leave you here, // there will be no further chance to reattempt sending. - for (vector::iterator b = blocked.begin(); b != blocked.end(); ++b) + for (vector::iterator b = blocked.begin(); b != blocked.end(); ++b) { (*b)->sndstate = SRT_GST_BROKEN; } @@ -1573,7 +1573,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // haven't sent the payload over any link so far, so we still have // a chance to retry. int modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - for (vector::iterator b = blocked.begin(); b != blocked.end(); ++b) + for (vector::iterator b = blocked.begin(); b != blocked.end(); ++b) { HLOGC(gslog.Debug, log << "Will block on blocked socket @" << (*b)->id << " as only blocked socket remained"); @@ -1657,7 +1657,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (stat != -1) curseq = w_mc.pktseq; - const Sendstate cstate = {d->id, d, stat, erc}; + const Sendstate cstate = {d->id, &*d, stat, erc}; sendstates.push_back(cstate); d->sndresult = stat; d->laststatus = d->ps->getStatus(); @@ -1671,7 +1671,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (is->stat == blocklen) { // Successful. - successful.push_back(is->it); + successful.push_back(is->mb); rstat = is->stat; was_blocked = false; none_succeeded = false; @@ -1684,7 +1684,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) << "). Setting this socket broken status."); #endif // Turn this link broken - is->it->sndstate = SRT_GST_BROKEN; + is->mb->sndstate = SRT_GST_BROKEN; } } } @@ -3250,7 +3250,7 @@ size_t CUDTGroup::sendBackup_CheckNeedActivate(const vector& idler d->sndresult = stat; d->laststatus = d->ps->getStatus(); - const Sendstate cstate = {d->id, d, stat, erc}; + const Sendstate cstate = {d->id, &*d, stat, erc}; w_sendstates.push_back(cstate); if (stat != -1) @@ -3920,7 +3920,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (is_unstable && is_zero(u.m_tsUnstableSince)) // Add to unstable only if it wasn't unstable already insert_uniq((unstable), d); - const Sendstate cstate = {d->id, d, stat, erc}; + const Sendstate cstate = {d->id, &*d, stat, erc}; sendstates.push_back(cstate); d->sndresult = stat; d->laststatus = d->ps->getStatus(); @@ -4331,7 +4331,7 @@ void CUDTGroup::ackMessage(int32_t msgno) m_iSndAckedMsgNo = msgno; } -void CUDTGroup::handleKeepalive(gli_t gli) +void CUDTGroup::handleKeepalive(CUDTGroup::SocketData* gli) { // received keepalive for that group member // In backup group it means that the link went IDLE. @@ -4368,7 +4368,7 @@ void CUDTGroup::handleKeepalive(gli_t gli) } } -void CUDTGroup::internalKeepalive(gli_t gli) +void CUDTGroup::internalKeepalive(SocketData* gli) { // This is in response to AGENT SENDING keepalive. This means that there's // no transmission in either direction, but the KEEPALIVE packet from the @@ -4457,18 +4457,18 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) UniqueLock lg(m_GroupLock); // Sanity check for a case when getting a deleted socket - if (!s->m_IncludedGroup) + if (!s->m_GroupOf) return; // Under a group lock, we block execution of removal of the socket - // from the group, so if m_IncludedGroup is not NULL, we are granted - // that m_IncludedIter is valid. - gli_t current = s->m_IncludedIter; + // from the group, so if m_GroupOf is not NULL, we are granted + // that m_GroupMemberData is valid. + SocketData* current = s->m_GroupMemberData; for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) { // Skip the socket that has reported packet reception - if (gi == current) + if (&*gi == current) { HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq on self @" << gi->id); continue; diff --git a/srtcore/group.h b/srtcore/group.h index 9e226bfd9..684e8ac0e 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -117,7 +117,7 @@ class CUDTGroup struct Sendstate { SRTSOCKET id; - gli_t it; + SocketData* mb; int stat; int code; }; @@ -127,7 +127,7 @@ class CUDTGroup static SocketData prepareData(CUDTSocket* s); - gli_t add(SocketData data); + SocketData* add(SocketData data); struct HaveID { @@ -139,15 +139,17 @@ class CUDTGroup bool operator()(const SocketData& s) { return s.id == id; } }; - gli_t find(SRTSOCKET id) + bool contains(SRTSOCKET id, SocketData*& w_f) { srt::sync::ScopedLock g(m_GroupLock); - gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id)); + gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id)); if (f == m_Group.end()) { - return gli_NULL(); + w_f = NULL; + return false; } - return f; + w_f = &*f; + return true; } // NEED LOCKING @@ -156,7 +158,7 @@ class CUDTGroup /// Remove the socket from the group container. /// REMEMBER: the group spec should be taken from the socket - /// (set m_IncludedGroup to NULL and m_IncludedIter to grp->gli_NULL()) + /// (set m_GroupOf and m_GroupMemberData to NULL /// PRIOR TO calling this function. /// @param id Socket ID to look for in the container to remove /// @return true if the container still contains any sockets after the operation @@ -219,8 +221,6 @@ class CUDTGroup void setGroupConnected(); - static gli_t gli_NULL() { return GroupContainer::null(); } - int send(const char* buf, int len, SRT_MSGCTRL& w_mc); int sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc); int sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc); @@ -346,8 +346,8 @@ class CUDTGroup #endif void ackMessage(int32_t msgno); - void handleKeepalive(gli_t); - void internalKeepalive(gli_t); + void handleKeepalive(SocketData*); + void internalKeepalive(SocketData*); private: // Check if there's at least one connected socket. @@ -362,7 +362,6 @@ class CUDTGroup struct GroupContainer { std::list m_List; - static std::list s_NoList; // This is to have a predictable "null iterator". /// This field is used only by some types of groups that need /// to keep track as to which link was lately used. Note that @@ -371,7 +370,7 @@ class CUDTGroup gli_t m_LastActiveLink; GroupContainer() - : m_LastActiveLink(s_NoList.begin()) + : m_LastActiveLink(m_List.end()) { } @@ -380,12 +379,11 @@ class CUDTGroup gli_t begin() { return m_List.begin(); } gli_t end() { return m_List.end(); } - static gli_t null() { return s_NoList.begin(); } bool empty() { return m_List.empty(); } void push_back(const SocketData& data) { m_List.push_back(data); } void clear() { - m_LastActiveLink = null(); + m_LastActiveLink = end(); m_List.clear(); } size_t size() { return m_List.size(); } From 21ad8d1fc5c2bdc8ed427ba0cfa3ac37b4904a86 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 18 Dec 2020 11:04:09 +0100 Subject: [PATCH 014/790] [core] Fixed miscalculations on extreme loss conditions in FEC (#1716) --- srtcore/fec.cpp | 553 ++++++++++++++++++++++++----------- srtcore/fec.h | 31 +- srtcore/packetfilter.cpp | 2 +- srtcore/packetfilter_api.h | 2 + test/test_fec_rebuilding.cpp | 4 +- 5 files changed, 417 insertions(+), 175 deletions(-) diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 308f45b75..36cb9b760 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -22,6 +22,15 @@ #include "fec.h" +// Maximum allowed "history" remembered in the receiver groups. +// This is calculated in series, that is, this number will be +// multiplied by sizeRow() and sizeCol() to get the value being +// a maximum distance between the FEC group base sequence and +// the sequence to which a request comes in. + +// XXX Might be that this parameter should be configurable +#define SRT_FEC_MAX_RCV_HISTORY 10 + using namespace std; using namespace srt_logging; @@ -100,7 +109,7 @@ FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector { if (level == levelnames[i]) { - lv = i; + lv = int(i); break; } } @@ -244,7 +253,7 @@ void FECFilterBuiltin::ConfigureColumns(Container& which, int32_t isn) for (size_t i = zero; i < which.size(); ++i) { - int32_t seq = CSeqNo::incseq(isn, offset); + int32_t seq = CSeqNo::incseq(isn, int(offset)); size_t col = i - zero; HLOGC(pflog.Debug, log << "ConfigureColumns: [" << col << "]: -> ConfigureGroup..."); @@ -293,7 +302,7 @@ void FECFilterBuiltin::ConfigureGroup(Group& g, int32_t seqno, size_t gstep, siz void FECFilterBuiltin::ResetGroup(Group& g) { - int32_t new_seq_base = CSeqNo::incseq(g.base, g.drop); + int32_t new_seq_base = CSeqNo::incseq(g.base, int(g.drop)); HLOGC(pflog.Debug, log << "FEC: ResetGroup (step=" << g.step << "): base %" << g.base << " -> %" << new_seq_base); @@ -377,7 +386,7 @@ void FECFilterBuiltin::feedSource(CPacket& packet) return; } - int vert_pos = vert_off / sizeRow(); + int vert_pos = vert_off / int(sizeRow()); HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() << " B:%" << baseoff << " H:*[" << horiz_pos << "] V(B=%" << vert_base @@ -429,7 +438,7 @@ void FECFilterBuiltin::ClipPacket(Group& g, const CPacket& pkt) // Both length and timestamp must be taken as NETWORK ORDER // before applying the clip. - uint16_t length_net = htons(pkt.size()); + uint16_t length_net = htons(uint16_t(pkt.size())); uint8_t kflg = uint8_t(pkt.getMsgCryptoFlags()); // NOTE: Unlike length, the TIMESTAMP is NOT endian-reordered @@ -478,7 +487,7 @@ void FECFilterBuiltin::ClipControlPacket(Group& g, const CPacket& pkt) void FECFilterBuiltin::ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt) { - uint16_t length_net = htons(pkt.length); + uint16_t length_net = htons(uint16_t(pkt.length)); uint8_t kflg = MSGNO_ENCKEYSPEC::unwrap(pkt.hdr[SRT_PH_MSGNO]); // NOTE: Unlike length, the TIMESTAMP is NOT endian-reordered @@ -558,7 +567,7 @@ bool FECFilterBuiltin::packControlPacket(SrtPacket& rpkt, int32_t seq) else { int offset_to_row_base = CSeqNo::seqoff(snd.row.base, seq); - int vert_gx = (offset_to_row_base + m_number_cols) % m_number_cols; + int vert_gx = (offset_to_row_base + int(m_number_cols)) % int(m_number_cols); // This can actually happen only for the very first sent packet. // It looks like "following the last packet from the previous group", @@ -719,6 +728,11 @@ bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) } HLOGC(pflog.Debug, log << "FEC: RECEIVED %" << rpkt.getSeqNo() << " msgno=0, FEC/CTL packet. INDEX=" << int(payload[0])); + + // This marks the cell as NOT received, but still does extend the + // cell container up to this sequence. The HangHorizontal and HangVertical + // functions that would also do cell dismissal, RELY ON IT. + MarkCellReceived(rpkt.getSeqNo(), CELL_EXTEND); } else { @@ -753,38 +767,58 @@ bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) loss_seqs_t irrecover_row, irrecover_col; - bool ok = true; +#if ENABLE_HEAVY_LOGGING + static string hangname [] = {"SUCCESS", "PAST", "CRAZY", "NOT-DONE"}; +#endif + + EHangStatus okh = HANG_NOTDONE; if (!isfec.col) // == regular packet or FEC/ROW { // Don't manage this packet for horizontal group, // if it was a vertical FEC/CTL packet. - ok = HangHorizontal(rpkt, isfec.row, irrecover_row); + okh = HangHorizontal(rpkt, isfec.row, irrecover_row); HLOGC(pflog.Debug, log << "FEC: HangHorizontal %" << rpkt.getSeqNo() << " msgno=" << rpkt.getMsgSeq() - << " RESULT=" << boolalpha << ok << " IRRECOVERABLE: " << Printable(irrecover_row)); + << " RESULT=" << hangname[okh] << " IRRECOVERABLE: " << Printable(irrecover_row)); } - if (!ok) + if (okh != HANG_SUCCESS) { // Just informative. LOGC(pflog.Warn, log << "FEC/H: rebuilding/hanging FAILED."); } + EHangStatus okv = HANG_NOTDONE; // Don't do HangVertical in case of row-only configuration if (!isfec.row && m_number_rows > 1) // == regular packet or FEC/COL { - ok = HangVertical(rpkt, isfec.colx, irrecover_col); + // NOTE FOR IPE REPORTING: + // It is allowed that + // - Both HangVertical and HangHorizontal + + okv = HangVertical(rpkt, isfec.colx, irrecover_col); + IF_HEAVY_LOGGING(bool discrep = (okv == HANG_CRAZY) ? int(okh) < HANG_CRAZY : false); HLOGC(pflog.Debug, log << "FEC: HangVertical %" << rpkt.getSeqNo() << " msgno=" << rpkt.getMsgSeq() - << " RESULT=" << boolalpha << ok << " IRRECOVERABLE: " << Printable(irrecover_col)); + << " RESULT=" << hangname[okh] + << (discrep ? " IPE: H successul and V failed!" : "") + << " IRRECOVERABLE: " << Printable(irrecover_col)); } - if (!ok) + if (okv != HANG_SUCCESS) { // Just informative. LOGC(pflog.Warn, log << "FEC/V: rebuilding/hanging FAILED."); } + if (okv == HANG_CRAZY || okh == HANG_CRAZY) + { + // Mark the cell not received, if it was rejected by the + // FEC group facility, otherwise it will deny to try to rebuild an + // allegedly existing packet. + MarkCellReceived(rpkt.getSeqNo(), CELL_REMOVE); + } + // Pack the following packets as irrecoverable: if (m_fallback_level == SRT_ARQ_ONREQ) { @@ -845,7 +879,7 @@ void FECFilterBuiltin::CheckLargeDrop(int32_t seqno) int32_t oldbase = rcv.rowq[0].base; size_t rowdist = offset / sizeRow(); - int32_t newbase = CSeqNo::incseq(oldbase, rowdist * sizeRow()); + int32_t newbase = CSeqNo::incseq(oldbase, int(rowdist * sizeRow())); LOGC(pflog.Warn, log << "FEC: LARGE DROP detected! Resetting row groups. Base: %" << oldbase << " -> %" << newbase << "(shift by " << CSeqNo::seqoff(oldbase, newbase) << ")"); @@ -886,16 +920,17 @@ void FECFilterBuiltin::CheckLargeDrop(int32_t seqno) return; } - size_t matrix = numberRows() * numberCols(); + const size_t size_in_packets = colx * numberRows(); + const size_t matrix = numberRows() * numberCols(); - int colseries = coloff / matrix; + const int colseries = coloff / int(matrix); - if (colseries > 2 || reset_anyway) + if (size_in_packets > rcvBufferSize()/2 || colseries > SRT_FEC_MAX_RCV_HISTORY || reset_anyway) { // Ok, now define the new ABSOLUTE BASE. This is the base of the column 0 // column group from the series previous towards this one. int32_t oldbase = rcv.colq[0].base; - int32_t newbase = CSeqNo::incseq(oldbase, (colseries-1) * matrix); + int32_t newbase = CSeqNo::incseq(oldbase, (colseries-1) * int(matrix)); LOGC(pflog.Warn, log << "FEC: LARGE DROP detected! Resetting all groups. Base: %" << oldbase << " -> %" << newbase << "(shift by " << CSeqNo::seqoff(oldbase, newbase) << ")"); @@ -939,7 +974,7 @@ void FECFilterBuiltin::CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) { LOGC(pflog.Error, log << "FEC: IPE: Collecting loss from row %" << g.base << "+" << m_number_cols << " while cells <= %" - << CSeqNo::seqoff(rcv.cell_base, rcv.cells.size()-1)); + << CSeqNo::seqoff(rcv.cell_base, int(rcv.cells.size())-1)); return; } @@ -953,11 +988,11 @@ void FECFilterBuiltin::CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) if (gone && !last) { // Switch full -> loss. Store the sequence, as single (for now) - val.first = val.second = CSeqNo::incseq(base, i); + val.first = val.second = CSeqNo::incseq(base, int(i)); } else if (last && !gone) { - val.second = CSeqNo::incseq(base, i); + val.second = CSeqNo::incseq(base, int(i)); irrecover.push_back(val); } } @@ -983,30 +1018,32 @@ static inline char CellMark(const std::deque& cells, int index) return cells[index] ? '#' : '.'; } -static void DebugPrintCells(int32_t base, const std::deque& cells, int row_size) +static void DebugPrintCells(int32_t base, const std::deque& cells, size_t row_size) { - int i = 0; + size_t i = 0; // Shift to the first empty cell - for ( ; i < int(cells.size()); ++i) + for ( ; i < cells.size(); ++i) if (cells[i] == false) break; - if (i == int(cells.size())) + if (i == cells.size()) { LOGC(pflog.Debug, log << "FEC: ... cell[0-" << (cells.size()-1) << "]: ALL CELLS EXIST"); return; } // Ok, we have some empty cells, so just adjust to the start of a row. - i -= i % row_size; - if (i < 0) - i = 0; // you never know... - - for ( ; i < int(cells.size()); i += row_size ) + size_t bstep = i % row_size; + if (i < bstep) // you never know... + i = 0; + else + i -= bstep; + + for ( ; i < cells.size(); i += row_size ) { std::ostringstream os; os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, i) << ":"; - for (int y = 0; y < row_size; ++y) + for (size_t y = 0; y < row_size; ++y) { os << " " << CellMark(cells, i+y); } @@ -1014,16 +1051,17 @@ static void DebugPrintCells(int32_t base, const std::deque& cells, int row } } #else -static void DebugPrintCells(int32_t /*base*/, const std::deque& /*cells*/, int /*row_size*/) {} +static void DebugPrintCells(int32_t /*base*/, const std::deque& /*cells*/, size_t /*row_size*/) {} #endif -bool FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs_t& irrecover) +FECFilterBuiltin::EHangStatus FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs_t& irrecover) { - int32_t seq = rpkt.getSeqNo(); + const int32_t seq = rpkt.getSeqNo(); - int rowx = RcvGetRowGroupIndex(seq); + EHangStatus stat; + const int rowx = RcvGetRowGroupIndex(seq, (stat)); if (rowx == -1) - return false; + return stat; RcvGroup& rowg = rcv.rowq[rowx]; // Clip the packet into the horizontal group. @@ -1094,9 +1132,9 @@ bool FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs } } - if (want_collect_irrecover) + if (want_collect_irrecover) // AND rcv.rowq.size() > 1 { - int current = rcv.rowq.size() - 2; + int current = int(rcv.rowq.size()) - 2; // We know we have at least 2 rows. // This value is then 0 or more. int past = current - 1; @@ -1143,17 +1181,21 @@ bool FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs CollectIrrecoverRow(rcv.rowq[i], irrecover); } - if (want_remove_cells) + // Sanity check condition - rcv.rowq must be of size + // greater than the number of rows to remove so that + // the rcv.rowq[0] exists after the operation. + if (want_remove_cells && rcv.rowq.size() > size_t(nrowremove)) { + // nrowremove >= 1 size_t npktremove = sizeRow() * nrowremove; - size_t ersize = min(npktremove, rcv.cells.size()); + size_t ersize = min(npktremove, rcv.cells.size()); // ersize <= rcv.cells.size() HLOGC(pflog.Debug, log << "FEC/H: Dismissing rows n=" << nrowremove << ", starting at %" << rcv.rowq[0].base << " AND " << npktremove << " CELLS, base switch %" << rcv.cell_base << " -> %" << rcv.rowq[past].base); - rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + 1 + past); + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + nrowremove); rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + ersize); // We state that we have removed as many cells as for the removed @@ -1172,7 +1214,7 @@ bool FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs } - return true; + return HANG_SUCCESS; } int32_t FECFilterBuiltin::RcvGetLossSeqHoriz(Group& g) @@ -1193,10 +1235,10 @@ int32_t FECFilterBuiltin::RcvGetLossSeqHoriz(Group& g) { if (!rcv.CellAt(cix)) { - offset = cix; + offset = int(cix); #if ENABLE_HEAVY_LOGGING // For heavy logging case, show all cells in the range - LOGC(pflog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + LOGC(pflog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, int(cix)) << " (+" << cix << "): MISSING"); #else @@ -1210,7 +1252,7 @@ int32_t FECFilterBuiltin::RcvGetLossSeqHoriz(Group& g) #if ENABLE_HEAVY_LOGGING else { - LOGC(pflog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + LOGC(pflog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, int(cix)) << " (+" << cix << "): exists"); } #endif @@ -1246,10 +1288,10 @@ int32_t FECFilterBuiltin::RcvGetLossSeqVert(Group& g) size_t cix = baseoff + (col * sizeRow()); if (!rcv.CellAt(cix)) { - offset = cix; + offset = int(cix); #if ENABLE_HEAVY_LOGGING // For heavy logging case, show all cells in the range - LOGC(pflog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + LOGC(pflog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, int(cix)) << " (+" << cix << "): MISSING"); #else @@ -1263,7 +1305,7 @@ int32_t FECFilterBuiltin::RcvGetLossSeqVert(Group& g) #if ENABLE_HEAVY_LOGGING else { - LOGC(pflog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + LOGC(pflog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, int(cix)) << " (+" << cix << "): exists"); } #endif @@ -1346,11 +1388,12 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) // This flips HORIZ/VERT Group::Type crosstype = Group::Type(!tp); + EHangStatus stat; if (crosstype == Group::HORIZ) { // Find this packet in the horizontal group - int rowx = RcvGetRowGroupIndex(seqno); + const int rowx = RcvGetRowGroupIndex(seqno, (stat)); if (rowx == -1) return; // can't access any group to rebuild RcvGroup& rowg = rcv.rowq[rowx]; @@ -1387,7 +1430,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) else // crosstype == Group::VERT { // Find this packet in the vertical group - int colx = RcvGetColumnGroupIndex(seqno); + const int colx = RcvGetColumnGroupIndex(seqno, (stat)); if (colx == -1) return; // can't access any group to rebuild RcvGroup& colg = rcv.colq[colx]; @@ -1424,7 +1467,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) } -int FECFilterBuiltin::ExtendRows(int rowx) +size_t FECFilterBuiltin::ExtendRows(size_t rowx) { // Check if oversize. Oversize is when the // index is > 2*m_number_cols. If so, shrink @@ -1437,17 +1480,14 @@ int FECFilterBuiltin::ExtendRows(int rowx) LOGC(pflog.Debug, log << "... [" << i << "] " << rcv.rowq[i].DisplayStats()); #endif - if (rowx > int(m_number_cols*3)) - { - LOGC(pflog.Warn, log << "FEC/H: OFFSET=" << rowx << " exceeds maximum row container size, SHRINKING rows and cells"); - - rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + m_number_cols); - rowx -= m_number_cols; + const size_t size_in_packets = rowx * numberCols(); + const int n_series = int(rowx / numberRows()); - // With rows, delete also an appropriate number of cells. - int nerase = min(int(rcv.cells.size()), CSeqNo::seqoff(rcv.cell_base, rcv.rowq[0].base)); - rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + nerase); - rcv.cell_base = rcv.rowq[0].base; + if (size_in_packets > rcvBufferSize() && n_series > 2) + { + HLOGC(pflog.Debug, log << "FEC: Emergency resize, rowx=" << rowx << " series=" << n_series + << "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize()); + EmergencyShrink(n_series); } // Create and configure next groups. @@ -1460,7 +1500,7 @@ int FECFilterBuiltin::ExtendRows(int rowx) for (size_t i = old; i < rcv.rowq.size(); ++i) { // Initialize the base for the row group - int32_t ibase = CSeqNo::incseq(rcv.rowq[0].base, i*m_number_cols); + int32_t ibase = CSeqNo::incseq(rcv.rowq[0].base, int(i*m_number_cols)); ConfigureGroup(rcv.rowq[i], ibase, 1, m_number_cols); } @@ -1474,17 +1514,18 @@ int FECFilterBuiltin::ExtendRows(int rowx) return rowx; } -int FECFilterBuiltin::RcvGetRowGroupIndex(int32_t seq) +int FECFilterBuiltin::RcvGetRowGroupIndex(int32_t seq, EHangStatus& w_status) { RcvGroup& head = rcv.rowq[0]; - int32_t base = head.base; + const int32_t base = head.base; - int offset = CSeqNo::seqoff(base, seq); + const int offset = CSeqNo::seqoff(base, seq); // Discard the packet, if older than base. if (offset < 0) { HLOGC(pflog.Debug, log << "FEC/H: Packet %" << seq << " is in the past, ignoring"); + w_status = HANG_PAST; return -1; } @@ -1510,18 +1551,20 @@ int FECFilterBuiltin::RcvGetRowGroupIndex(int32_t seq) // First, possibly extend the row container if (rowx >= rcv.rowq.size()) { + // Never returns -1 rowx = ExtendRows(rowx); } - return rowx; + w_status = HANG_SUCCESS; + return int(rowx); } -void FECFilterBuiltin::MarkCellReceived(int32_t seq) +void FECFilterBuiltin::MarkCellReceived(int32_t seq, ECellReceived is_received) { // Mark the packet as received. This will allow later to // determine, which exactly packet is lost and needs rebuilding. - int cellsize = rcv.cells.size(); - int cell_offset = CSeqNo::seqoff(rcv.cell_base, seq); + const int cellsize = int(rcv.cells.size()); + const int cell_offset = CSeqNo::seqoff(rcv.cell_base, seq); bool resized SRT_ATR_UNUSED = false; if (cell_offset >= cellsize) { @@ -1531,48 +1574,179 @@ void FECFilterBuiltin::MarkCellReceived(int32_t seq) resized = true; rcv.cells.resize(cell_offset+1, false); } - rcv.cells[cell_offset] = true; - HLOGC(pflog.Debug, log << "FEC: MARK CELL RECEIVED: %" << seq << " - cells base=%" + if (resized || is_received != CELL_EXTEND) + { + // In both RECEIVED and REMOVE cases, forcefully set the value always. + // In EXTEND, only if it was received + // Value set should be true only if RECEIVED, false otherwise + rcv.cells[cell_offset] = (is_received == CELL_RECEIVED); + } + +#if ENABLE_HEAVY_LOGGING + static string const cellop [] = { "RECEIVED", "EXTEND", "REMOVE" }; + LOGC(pflog.Debug, log << "FEC: MARK CELL " << cellop[is_received] + << "(" << (rcv.cells[cell_offset] ? "SET" : "CLR") << ")" + << ": %" << seq << " - cells base=%" << rcv.cell_base << "[" << cell_offset << "]+" << rcv.cells.size() << (resized ? "(resized)":"") << " :"); +#endif DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); } bool FECFilterBuiltin::IsLost(int32_t seq) const { - int offset = CSeqNo::seqoff(rcv.cell_base, seq); + const int offset = CSeqNo::seqoff(rcv.cell_base, seq); if (offset < 0) { LOGC(pflog.Error, log << "FEC: IsLost: IPE: %" << seq << " is earlier than the cell base %" << rcv.cell_base); - return true; // fake we have the packet - this is to collect losses only + return true; // This might be due to emergency shrinking; pretend the packet is lost } if (offset >= int(rcv.cells.size())) { // XXX IPE! LOGC(pflog.Error, log << "FEC: IsLost: IPE: %" << seq << " is past the cells %" << rcv.cell_base << " + " << rcv.cells.size()); - return true; + return false; // Don't notify it yet } return rcv.cells[offset]; } -bool FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, loss_seqs_t& irrecover) +void FECFilterBuiltin::EmergencyShrink(size_t n_series) +{ + // Shrink is required in order to prepare place for + // either vertical or horizontal group in series `n_series`. + + // The n_series can be calculated as: + // n_series = colgx / numberCols() + // n_series = rowgx / numberRows() + // + // The (Column or Row) Group Index value is calculated as + // the number of column where the desired sequence number + // should be located towards the very first container item + // (row/column 0). + + // The task for this function is to leave only one series + // of groups and therefore initialize the containers. Likely + // the part that contains the last series should be already + // there, so in this case just remove some initial items from + // the container so that only those remain that are intended + // to remain. However, by various reasons (like e.g. that all + // packets from the whole series have been lost) particular + // container (colq, rowq, cell) doesn't contain this last + // series at all. In that case clear the container completely + // and just add an initial configuration for the first part + // (which will be then dynamically extended as packets come in). + + const int32_t oldbase = rcv.colq[0].base; + const size_t shift_series = n_series - 1; + + // This is simply a situation when the size is so excessive + // that it couldn't be withstood by the receiver buffer, so + // even if this isn't an extremely big size for allocation for + // FEC, it doesn't make sense anyway. + // + // Minimum of 2 series must remain in the group container, + // otherwise there's no need to guard the size. + + // This requires simply resetting all group containers to + // the very initial state, just take the calculated base seq + // from the value of colgx reset to column 0. + + // As colgx is calculated by stating that colgx == 0 represents + // the very first cell in the column groups, take this, shift + // by the number of series. + + // SHIFT BY: n_series * matrix size + // n_series is at least 2 (see condition) + const size_t shift = shift_series * numberCols() * numberRows(); + + // Always positive: colgx, and so n_series, and so shift + const int32_t newbase = CSeqNo::incseq(oldbase, int(shift)); + + const size_t shift_rows = shift_series * numberRows(); + + bool need_reset = rcv.rowq.size() < shift_rows; + if (!need_reset) + { + // Sanity check - you should have the exact value + // of `newbase` at the next series beginning position + if (rcv.rowq[numberRows()].base != newbase) + { + LOGC(pflog.Error, log << "FEC: IPE: row start at %" << rcv.rowq[0].base << " next series %" << rcv.rowq[numberRows()].base + << " (expected %" << newbase << "). RESETTING ROWS."); + need_reset = true; + } + } + + if (need_reset) + { + rcv.rowq.clear(); + // This n_series is the number rounded downwards, + // So you just need to prepare place for ONE series. + // The procedure below will extend them to the required + // size for the received colgx. + rcv.rowq.resize(1); + + HLOGC(pflog.Debug, log << "FEC: Reset recv row %" << oldbase << " -> %" << newbase << ", INIT ROWS:"); + ConfigureGroup(rcv.rowq[0], newbase, 1, sizeRow()); + } + else + { + HLOGC(pflog.Debug, log << "FEC: Shifting rcv row %" << oldbase << " -> %" << newbase); + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.end() + shift_rows); + } + + const size_t shift_cols = shift_series * numberCols(); + need_reset = rcv.colq.size() < shift_cols; + if (!need_reset) + { + // Sanity check - you should have the exact value + // of `newbase` at the next series beginning position + if (rcv.colq[numberCols()].base != newbase) + { + LOGC(pflog.Error, log << "FEC: IPE: col start at %" << rcv.colq[0].base << " next series %" << rcv.colq[numberCols()].base + << " (expected %" << newbase << "). RESETTING ROWS."); + need_reset = true; + } + } + + if (need_reset) + { + rcv.colq.clear(); + HLOGC(pflog.Debug, log << "FEC: Reset recv row %" << oldbase << " -> %" << newbase << ", INIT first " << numberCols() << ":"); + ConfigureColumns(rcv.colq, newbase); + } + + if (rcv.cells.size() > shift) + { + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + shift); + } + else + { + rcv.cells.clear(); + rcv.cells.push_back(false); + } + rcv.cell_base = newbase; +} + +FECFilterBuiltin::EHangStatus FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, loss_seqs_t& irrecover) { bool fec_ctl = (fec_col != -1); // Now hang the packet in the vertical group - int32_t seq = rpkt.getSeqNo(); + const int32_t seq = rpkt.getSeqNo(); // Ok, now we have the column index, we know it exists. // Apply the packet. - int colgx = RcvGetColumnGroupIndex(seq); + EHangStatus stat; + const int colgx = RcvGetColumnGroupIndex(seq, (stat)); if (colgx == -1) - return false; + return stat; RcvGroup& colg = rcv.colq[colgx]; @@ -1618,7 +1792,7 @@ bool FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, lo LOGC(pflog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); #endif - return true; + return HANG_SUCCESS; } void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t& irrecover) @@ -1629,7 +1803,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t // - get the series for this column // - if series is 0, just return - int series = colgx / numberCols(); + const size_t series = colgx / numberCols(); if (series == 0) return; @@ -1640,7 +1814,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t set loss; - int colx SRT_ATR_UNUSED = colgx % numberCols(); + size_t colx SRT_ATR_UNUSED = colgx % numberCols(); HLOGC(pflog.Debug, log << "FEC/V: going to DISMISS cols past %" << seq << " at INDEX=" << colgx << " col=" << colx @@ -1667,7 +1841,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t // because they can't be dismissed yet. Jump them over, so maybe // they can be dismissed in future. int this_col_offset = CSeqNo::seqoff(pg.base, seq); - int last_seq_offset = this_col_offset - (sizeCol()-1)*sizeRow(); + int last_seq_offset = this_col_offset - int((sizeCol()-1)*sizeRow()); if (last_seq_offset < 0) { @@ -1690,7 +1864,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t pg.dismissed = true; // mark irrecover already collected for (size_t sof = 0; sof < pg.step * sizeCol(); sof += pg.step) { - int32_t lseq = CSeqNo::incseq(pg.base, sof); + int32_t lseq = CSeqNo::incseq(pg.base, int(sof)); if (!IsLost(lseq)) { loss.insert(lseq); @@ -1721,31 +1895,46 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t int32_t base0 = rcv.colq[0].base; int this_off = CSeqNo::seqoff(base0, seq); - int mindist = + int mindist = int( m_arrangement_staircase ? (numberCols() * numberRows() * 2) : - (numberCols() * numberRows()); + (numberCols() * numberRows())); bool any_dismiss SRT_ATR_UNUSED = false; + // Here's a change. + // The number of existing column groups is supposed to always cover + // at least one full series, whereas the number of row groups are + // created always one per necessity, so the number of existing row + // groups may be less than required for a full series, whereas here + // it is intended to simply dismiss groups for full series. This may + // cause that it is aiming for removing more row groups than currently + // exist. This is completely ok, as the sequence that triggered removal + // is long past these series anyway, so the groups for packets that will + // never be received makes no sense. Simply accept this state and delete + // all row groups and reinitialize them into the new base, where the base + // is the current base for column 0 group. + // + // Therefore dismissal is triggered whenever you have a cover of one column + // series. If the number of row groups doesn't cover it, simply delete all + // row groups, that's all. + // if (base0 +% mindist) <% seq - if (this_off < mindist) + if (this_off < mindist) // COND 1: minimum remaining { HLOGC(pflog.Debug, log << "FEC/V: NOT dismissing any columns at %" << seq << ", need to pass %" << CSeqNo::incseq(base0, mindist)); } - else if (rcv.colq.size() < numberCols()) + else if (rcv.colq.size() - 1 < numberCols()) // COND 2: full matrix in columns { - HLOGC(pflog.Debug, log << "FEC/V: IPE: about to dismiss past %" << seq - << " with required %" << CSeqNo::incseq(base0, mindist) - << " but col container size still " << rcv.colq.size()); - } - else if (rcv.rowq.size() < numberRows()) - { - HLOGC(pflog.Debug, log << "FEC/V: IPE: about to dismiss past %" << seq +#if ENABLE_HEAVY_LOGGING + LOGC(pflog.Debug, log << "FEC/V: IPE: about to dismiss past %" << seq << " with required %" << CSeqNo::incseq(base0, mindist) - << " but row container size still " << rcv.rowq.size()); + << " but col container size still " << rcv.colq.size() << "; COL STATS:"); + for (size_t i = 0; i < rcv.colq.size(); ++i) + LOGC(pflog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); +#endif } else { @@ -1753,9 +1942,30 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t // is numberCols(), regardless of the required 'mindinst'. any_dismiss = true; - int32_t newbase = rcv.colq[numberCols()].base; - int32_t newbase_row = rcv.rowq[numberRows()].base; - int matrix_size = numberCols() * numberRows(); + const int32_t newbase = rcv.colq[numberCols()].base; + int32_t newbase_row ATR_UNUSED; // For logging only, but including FATAL. + // Sanity check + // If sanity check failed OR if the number of existing row + // groups doesn't enclose those that need to be dismissed, + // clear row groups completely - these packets are lost and + // irrecoverable anyway. + bool insane = false; + bool undercounted = false; + + if (rcv.rowq.size() - 1 < numberRows()) // COND 3: full matrix in rows + { + // Do not reach to index=numberRows() because it doesn't exist. + // Take the value from the columns as a good deal - actually + // row base and col base shall be always in sync. + newbase_row = newbase; + undercounted = true; + } + else + { + newbase_row = rcv.rowq[numberRows()].base; + insane = newbase_row != newbase; + } + const size_t matrix_size = numberCols() * numberRows(); HLOGC(pflog.Debug, log << "FEC/V: DISMISSING " << numberCols() << " COLS. Base %" << rcv.colq[0].base << " -> %" << newbase @@ -1763,6 +1973,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t << rcv.rowq[0].base << " -> %" << newbase_row << " AND " << matrix_size << " cells"); + // ensured existence of the removed range: see COND 2 above. rcv.colq.erase(rcv.colq.begin(), rcv.colq.begin() + numberCols()); #if ENABLE_HEAVY_LOGGING @@ -1773,11 +1984,24 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t #endif // Now erase accordingly one matrix of rows. - // Sanity check - if (newbase_row != newbase) + if (insane || undercounted) { - LOGC(pflog.Fatal, log << "FEC/V: IPE: DISCREPANCY in base0 col=%" - << newbase << " row=%" << newbase_row << " - DELETING ALL ROWS"); + if (insane) + { + LOGC(pflog.Fatal, log << "FEC/V: IPE: DISCREPANCY in new base0 col=%" + << newbase << " row=%" << newbase_row << " - DELETING ALL ROWS"); + } + else + { + +#if ENABLE_HEAVY_LOGGING + LOGC(pflog.Debug, log << "FEC/V: about to dismiss past %" << seq + << " with required %" << CSeqNo::incseq(base0, mindist) + << " but row container size still " << rcv.rowq.size() << " (will clear to %" << newbase << " instead); ROW STATS:"); + for (size_t i = 0; i < rcv.rowq.size(); ++i) + LOGC(pflog.Debug, log << "... [" << i << "] " << rcv.rowq[i].DisplayStats()); +#endif + } // Delete all rows and reinitialize them. rcv.rowq.clear(); @@ -1787,27 +2011,31 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t else { // Remove "legally" a matrix of rows. + // ensured existence of the removed range: see COND 3 above rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + numberRows()); } // And now accordingly remove cells. Exactly one matrix of cells. // Sanity check first. - int32_t newbase_cell = CSeqNo::incseq(rcv.cell_base, matrix_size); + int32_t newbase_cell = CSeqNo::incseq(rcv.cell_base, int32_t(matrix_size)); if (newbase != newbase_cell) { - LOGC(pflog.Fatal, log << "FEC/V: IPE: DISCREPANCY in base0 col=%" - << newbase << " row=%" << newbase_row << " - DELETING ALL ROWS"); + LOGC(pflog.Fatal, log << "FEC/V: IPE: DISCREPANCY in new base0 col=%" + << newbase << " cell_base=%" << newbase_cell << " - DELETING ALL CELLS"); // Try to shift it gently first. Find the cell that matches the base. int shift = CSeqNo::seqoff(rcv.cell_base, newbase); - if (shift < 0) + if (shift < 0 || size_t(shift) > rcv.cells.size()) rcv.cells.clear(); else rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + shift); } else { - rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + matrix_size); + if (rcv.cells.size() <= size_t(matrix_size)) + rcv.cells.clear(); + else + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + matrix_size); } rcv.cell_base = newbase; DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); @@ -1974,7 +2202,7 @@ void FECFilterBuiltin::TranslateLossRecords(const set& loss, loss_seqs_ irrecover.push_back(make_pair(fi_start, fi_end)); } -int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) +int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno, EHangStatus& w_status) { // The column is only the column, not yet // exactly the index of the column group in the container. @@ -2048,32 +2276,35 @@ int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) // // GROUP_INDEX = COLUMN_INDEX + (COLUMN_SERIES * m_number_cols) - int offset = CSeqNo::seqoff(rcv.colq[0].base, seqno); + const int offset = CSeqNo::seqoff(rcv.colq[0].base, seqno); if (offset < 0) { HLOGC(pflog.Debug, log << "FEC/V: %" << seqno << " in the past of col ABSOLUTE base %" << rcv.colq[0].base); + w_status = HANG_PAST; return -1; } if (offset > CSeqNo::m_iSeqNoTH/2) { LOGC(pflog.Error, log << "FEC/V: IPE/ATTACK: pkt %" << seqno << " has CRAZY OFFSET towards the base %" << rcv.colq[0].base); + w_status = HANG_CRAZY; return -1; } - int colx = offset % m_number_cols; - int32_t colbase = rcv.colq[colx].base; - int coloff = CSeqNo::seqoff(colbase, seqno); + const int colx = offset % m_number_cols; + const int32_t colbase = rcv.colq[colx].base; + const int coloff = CSeqNo::seqoff(colbase, seqno); if (coloff < 0) { HLOGC(pflog.Debug, log << "FEC/V: %" << seqno << " in the past of col #" << colx << " base %" << colbase); // This means that this sequence number predates the earliest // sequence number supported by the very first column. + w_status = HANG_PAST; return -1; } - int colseries = coloff / (m_number_cols * m_number_rows); - size_t colgx = colx + (colseries * m_number_cols); + const int colseries = coloff / int(m_number_cols * m_number_rows); + size_t colgx = colx + int(colseries * m_number_cols); HLOGC(pflog.Debug, log << "FEC/V: Lookup group for %" << seqno << ": cg_base=%" << rcv.colq[0].base << " column=" << colx << " with base %" << colbase << ": SERIES=" << colseries @@ -2081,10 +2312,11 @@ int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) // Check oversize. Dismiss some earlier items if it exceeds the size. // before you extend the size enormously. - if (colgx > m_number_rows * m_number_cols * 2) + if (colgx > m_number_rows * m_number_cols * SRT_FEC_MAX_RCV_HISTORY) { // That's too much LOGC(pflog.Error, log << "FEC/V: IPE or ATTACK: offset " << colgx << " is too crazy, ABORTING lookup"); + w_status = HANG_CRAZY; return -1; } @@ -2092,8 +2324,8 @@ int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) { colgx = ExtendColumns(colgx); } - - return colgx; + w_status = HANG_SUCCESS; + return int(colgx); // // Even though column groups are arranged in a "staircase", it only means @@ -2146,56 +2378,45 @@ int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) // gmax = SHIFT(g.base, m_number_cols * m_number_rows) // IF ( gs %> gmax ) // DISMISS COLUMNS from 0 to GROUP_INDEX - i; break - } -int FECFilterBuiltin::ExtendColumns(int colgx) +size_t FECFilterBuiltin::ExtendColumns(size_t colgx) { - if (colgx > int(sizeRow() * 2)) + // This isn't safe to allow the group container to get expanded to any + // size, however with some very tolerant settings, such as 10 seconds of + // latency and very large receiver buffer, this might be tolerable. + // + // Therefore put only two conditions here: + // + // 1. The group containers must keep at most place for so many + // packets as it is intended for the receiver buffer. Keeping + // group cells for more packets doesn't make sense anyway. + // + // 2. Existing group containers should contain at least size + // for two series. If they don't contain that much, there's no + // need to do any emergency shrinking. Unknown whether this is + // physically possible, although it may also happen in case when + // you have very large FEC matrix size not coordinated with the + // receiver buffer size. + + // colgx is the number of column + NSERIES * numberCols(). + // We can state that for every column we should have a number + // of packets as many as the number of rows, so simply multiply this. + const size_t size_in_packets = colgx * numberRows(); + const size_t n_series = colgx / numberCols(); + if (size_in_packets > rcvBufferSize()/2 || n_series > SRT_FEC_MAX_RCV_HISTORY) + { + HLOGC(pflog.Debug, log << "FEC: Emergency resize, colgx=" << colgx << " series=" << n_series + << "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize()); + EmergencyShrink(n_series); + } + else { - // This shouldn't happen because columns should be dismissed - // once the last row of the first series is closed. - LOGC(pflog.Warn, log << "FEC/V: OFFSET=" << colgx << " exceeds maximum col container size, SHRINKING container by " << sizeRow()); - - // Delete one series of columns. - int32_t oldbase SRT_ATR_UNUSED = rcv.colq[0].base; - rcv.colq.erase(rcv.colq.begin(), rcv.colq.begin() + numberCols()); - colgx -= numberCols(); - int32_t newbase = rcv.colq[0].base; - - // Delete also appropriate number of rows for one series - rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + numberRows()); - - // Sanity-check if the resulting row absolute base is equal to column - if (rcv.rowq[0].base != newbase) - { - LOGC(pflog.Error, log << "FEC/V: IPE: removal of " << numberRows() - << " rows ships no same seq: rowbase=%" - << rcv.rowq[0].base - << " colbase=%" << oldbase << " -> %" << newbase << " - RESETTING ROWS"); - - // How much you need, depends on the columns. - size_t nseries = rcv.colq.size() / numberCols() + 1; - size_t needrows = nseries * numberRows(); - - rcv.rowq.clear(); - rcv.rowq.resize(needrows); - int32_t rowbase = newbase; - for (size_t i = 0; i < rcv.rowq.size(); ++i) - { - ConfigureGroup(rcv.rowq[i], rowbase, 1, sizeRow()); - rowbase = CSeqNo::incseq(newbase, sizeRow()); - } - } - - size_t ncellrem = CSeqNo::seqoff(rcv.cell_base, newbase); - rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + ncellrem); - rcv.cell_base = newbase; - - // Note that after this shift, column groups that were - // in particular column, remain in that column. + HLOGC(pflog.Debug, log << "FEC: Will extend up to colgx=" << colgx << " series=" << n_series + << " for npackets=" << size_in_packets); } + #if ENABLE_HEAVY_LOGGING LOGC(pflog.Debug, log << "FEC: COL STATS BEFORE: n=" << rcv.colq.size()); @@ -2204,11 +2425,11 @@ int FECFilterBuiltin::ExtendColumns(int colgx) #endif // First, obtain the "series" of columns, possibly fixed. - int series = colgx / numberCols(); + const int series = int(colgx / numberCols()); // Now, the base of the series is the base increased by one matrix size. - int32_t base = rcv.colq[0].base; + const int32_t base = rcv.colq[0].base; // This is the base for series 0, but this procedure must be prepared // for that the series will not necessarily be 1, may be greater. @@ -2218,7 +2439,7 @@ int FECFilterBuiltin::ExtendColumns(int colgx) // Check, up to which series the columns are initialized. // Start with the series that doesn't exist - int old_series = rcv.colq.size() / numberCols(); + const int old_series = int(rcv.colq.size() / numberCols()); // Each iteration of this loop adds one series of columns. // One series count numberCols() columns. @@ -2231,7 +2452,7 @@ int FECFilterBuiltin::ExtendColumns(int colgx) // Every base sequence for a series of columns is the series 0 // base increased by one matrix size times series number. // THIS REMAINS TRUE NO MATTER IF WE USE STRAIGNT OR STAIRCASE ARRANGEMENT. - int32_t sbase = CSeqNo::incseq(base, (numberCols()*numberRows()) * s); + const int32_t sbase = CSeqNo::incseq(base, int(numberCols()*numberRows()) * s); HLOGC(pflog.Debug, log << "FEC/V: EXTENDING column groups series " << s << ", size " << rcv.colq.size() << " -> " << (rcv.colq.size() + numberCols()) diff --git a/srtcore/fec.h b/srtcore/fec.h index 1fa158fff..795de658f 100644 --- a/srtcore/fec.h +++ b/srtcore/fec.h @@ -183,21 +183,38 @@ class FECFilterBuiltin: public SrtPacketFilterBase // Receiving void CheckLargeDrop(int32_t seqno); - int ExtendRows(int rowx); - int ExtendColumns(int colgx); - void MarkCellReceived(int32_t seq); - bool HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover); - bool HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover); + size_t ExtendRows(size_t rowx); + size_t ExtendColumns(size_t colgx); + + enum ECellReceived + { + CELL_RECEIVED, //< mark cell for a received packet (no matter current value) + CELL_EXTEND, //< just make sure there's a place for a packet, set false if not + CELL_REMOVE //< even if a packet was marked true, remove the cell existence confirmation + }; + void MarkCellReceived(int32_t seq, ECellReceived recv = CELL_RECEIVED); + + enum EHangStatus + { + HANG_SUCCESS, + HANG_PAST, + HANG_CRAZY, + HANG_NOTDONE + }; + + EHangStatus HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover); + EHangStatus HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover); void ClipControlPacket(Group& g, const CPacket& pkt); void ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt); void RcvRebuild(Group& g, int32_t seqno, Group::Type tp); int32_t RcvGetLossSeqHoriz(Group& g); int32_t RcvGetLossSeqVert(Group& g); + void EmergencyShrink(size_t n_series); static void TranslateLossRecords(const std::set& loss, loss_seqs_t& irrecover); void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover); - int RcvGetRowGroupIndex(int32_t seq); - int RcvGetColumnGroupIndex(int32_t seq); + int RcvGetRowGroupIndex(int32_t seq, EHangStatus& w_status); + int RcvGetColumnGroupIndex(int32_t seqno, EHangStatus& w_status); void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const; bool IsLost(int32_t seq) const; diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 993dc806b..121a717d1 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -246,7 +246,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); init.payload_size = parent->OPT_PayloadSize(); - + init.rcvbuf_size = parent->m_iRcvBufSize; // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 1f5d7ff23..07ce3392d 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -58,6 +58,7 @@ struct SrtFilterInitializer int32_t snd_isn; int32_t rcv_isn; size_t payload_size; + size_t rcvbuf_size; }; struct SrtPacket @@ -91,6 +92,7 @@ class SrtPacketFilterBase int32_t sndISN() const { return initParams.snd_isn; } int32_t rcvISN() const { return initParams.rcv_isn; } size_t payloadSize() const { return initParams.payload_size; } + size_t rcvBufferSize() const { return initParams.rcvbuf_size; } friend class PacketFilter; diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 8651a341e..dd3d9828c 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "packet.h" #include "fec.h" +#include "core.h" #include "packetfilter.h" #include "packetfilter_api.h" @@ -32,7 +33,8 @@ class TestFECRebuilding: public testing::Test sockid, isn - 1, // It's passed in this form to PacketFilter constructor, it should increase it isn - 1, // XXX Probably this better be changed. - plsize + plsize, + CUDT::DEF_BUFFER_SIZE }; From 60a66a311d36cfbb557ff1e90871c0ce0247af7f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 22 Dec 2020 11:53:49 +0100 Subject: [PATCH 015/790] [core] Set closing state for a broken link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Małecki --- srtcore/core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 38a492e6e..2652e49d5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11276,6 +11276,7 @@ void CUDT::checkTimers() void CUDT::updateBrokenConnection() { + m_bClosing = true; releaseSynch(); // app can call any UDT API to learn the connection_broken error s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); From de57eca687aa68fde5d488ada6fb9f4b23741038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 18 Dec 2020 13:13:47 +0100 Subject: [PATCH 016/790] [core] Fixes potential exception slipup on memory allocation error --- srtcore/sync_posix.cpp | 75 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 01818ae6c..8c001ad62 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -444,45 +444,84 @@ class CThreadError public: CThreadError() { - pthread_key_create(&m_TLSError, TLSDestroy); + pthread_key_create(&m_ThreadSpecKey, ThreadSpecKeyDestroy); + + // This is a global object and as such it should be called in the + // main application thread or at worst in the thread that has first + // run `srt_startup()` function and so requested the SRT library to + // be dynamically linked. Most probably in this very thread the API + // errors will be reported, so preallocate the ThreadLocalSpecific + // object for this error description. + + // This allows std::bac_alloc to crash the program during + // the initialization of the SRT library (likely it would be + // during the DL constructor, still way before any chance of + // doing any operations here). This will prevent SRT from running + // into trouble while trying to operate. + CUDTException* ne = new CUDTException(); + pthread_setspecific(m_ThreadSpecKey, ne); } ~CThreadError() { - delete (CUDTException*)pthread_getspecific(m_TLSError); - pthread_key_delete(m_TLSError); + // Likely all objects should be deleted in all + // threads that have exited, but std::this_thread didn't exit + // yet :). + ThreadSpecKeyDestroy(pthread_getspecific(m_ThreadSpecKey)); + pthread_key_delete(m_ThreadSpecKey); } -public: void set(const CUDTException& e) { CUDTException* cur = get(); - SRT_ASSERT(cur != NULL); + // If this returns NULL, it means that there was an unexpected + // memory allocation error. Simply ignore this request if so + // happened, and then when trying to get the error description + // the application will always get the memory allocation error. + + // There's no point in doing anything else here; lack of memory + // must be prepared for prematurely, and that was already done. + if (!cur) + return; + *cur = e; } - CUDTException* get() + /*[[nullable]]*/ CUDTException* get() { - if (!pthread_getspecific(m_TLSError)) + if (!pthread_getspecific(m_ThreadSpecKey)) { - CUDTException* ne = new CUDTException(); - pthread_setspecific(m_TLSError, ne); + // This time if this can't be done due to memory allocation + // problems, just allow this value to be NULL, which during + // getting the error description will redirect to a memory + // allocation error. + + // It would be nice to somehow ensure that this object is + // created in every thread of the application using SRT, but + // POSIX thread API doesn't contain any possibility to have + // a creation callback that would apply to every thread in + // the application (as it is for C++11 thread_local storage). + CUDTException* ne = new(std::nothrow) CUDTException(); + pthread_setspecific(m_ThreadSpecKey, ne); return ne; } - return (CUDTException*)pthread_getspecific(m_TLSError); + return (CUDTException*)pthread_getspecific(m_ThreadSpecKey); } - static void TLSDestroy(void* e) + static void ThreadSpecKeyDestroy(void* e) { delete (CUDTException*)e; } private: - pthread_key_t m_TLSError; + pthread_key_t m_ThreadSpecKey; }; // Threal local error will be used by CUDTUnited // that has a static scope + +// This static makes this object file-private access so that +// the access is granted only for the accessor functions. static CThreadError s_thErr; void SetThreadLocalError(const CUDTException& e) @@ -492,7 +531,17 @@ void SetThreadLocalError(const CUDTException& e) CUDTException& GetThreadLocalError() { - return *s_thErr.get(); + // In POSIX version we take into account the possibility + // of having an allocation error here. Therefore we need to + // allow thie value to return NULL and have some fallback + // for that case. The dynamic memory allocation failure should + // be the only case as to why it is unable to get the pointer + // to the error description. + static CUDTException resident_alloc_error (MJ_SYSTEMRES, MN_MEMORY); + CUDTException* curx = s_thErr.get(); + if (!curx) + return resident_alloc_error; + return *curx; } } // namespace sync From a165d459c99b8d9188f4063088ecd300793b0ba3 Mon Sep 17 00:00:00 2001 From: gururajrkatti <74309971+gururajrkatti@users.noreply.github.com> Date: Wed, 23 Dec 2020 20:27:17 +0530 Subject: [PATCH 017/790] [build] Add ppc64le platform to CI (#1717) --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 04c9319ec..151187e64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,6 +54,16 @@ matrix: - make - cd .. env: BUILD_TYPE=Release + + # Power jobs + - os: linux + arch: ppc64le + env: + - BUILD_TYPE=Debug + - arch: ppc64le + env: + - BUILD_TYPE=Release + - BUILD_OPTS='-DENABLE_MONOTONIC_CLOCK=ON' script: - if [ "$TRAVIS_COMPILER" == "x86_64-w64-mingw32-g++" ]; then export CC="x86_64-w64-mingw32-gcc"; From df25ca82d46ec6ab3ae2928b7ecee470afa0bf80 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 23 Dec 2020 19:31:39 +0100 Subject: [PATCH 018/790] [core] Minor warning fixes (C4267): type conversion with possible loss of data (#1710) --- srtcore/api.cpp | 4 ++-- srtcore/buffer.cpp | 4 ++-- srtcore/core.cpp | 20 ++++++++++---------- srtcore/fec.cpp | 4 ++-- srtcore/packet.cpp | 2 +- srtcore/packet.h | 2 +- srtcore/queue.cpp | 14 +++++++------- srtcore/queue.h | 4 ++-- srtcore/window.h | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5d0aa2904..71fae7e61 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1413,7 +1413,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar { HLOGC(aclog.Debug, log << "groupConnect: OPTION @" << sid << " #" << g.m_config[i].so); error_reason = "setting group-derived option: #" + Sprint(g.m_config[i].so); - ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], g.m_config[i].value.size()); + ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], (int) g.m_config[i].value.size()); } // Do not try to set a user option if failed already. @@ -1700,7 +1700,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar break; } HLOGC(aclog.Debug, log << "groupConnect: first connection, applying EPOLL WAITING."); - int len = spawned.size(); + int len = (int) spawned.size(); vector ready(spawned.size()); const int estat = srt_epoll_wait(eid, NULL, NULL, // IN/ACCEPT diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index a3654eb92..89c2dab72 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -994,7 +994,7 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) #if ENABLE_LOGGING trace_seq = pkt.getSeqNo(); #endif - const int pktlen = pkt.getLength(); + const int pktlen = (int) pkt.getLength(); const int remain_pktlen = pktlen - m_iNotch; const int unitsize = std::min(remain_pktlen, rs); @@ -2295,7 +2295,7 @@ bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) } rmpkts++; - rmbytes += freeUnitAt(m_iStartPos); + rmbytes += (int) freeUnitAt((size_t) m_iStartPos); m_iStartPos = shiftFwd(m_iStartPos); } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2652e49d5..59076d3d2 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1378,7 +1378,7 @@ bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t unsigned char* mem = new unsigned char[headersize + payload]; SingleOption* option = reinterpret_cast(mem); option->option = optname; - option->length = optlen; + option->length = (uint16_t) optlen; memcpy(option->storage, optval, optlen); options.push_back(option); @@ -1872,7 +1872,7 @@ size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& st // Preswap to little endian (in place due to possible padding zeros) HtoILA((space), space, wordsize); - *pcmdspec = HS_CMDSPEC_CMD::wrap(cmd) | HS_CMDSPEC_SIZE::wrap(wordsize); + *pcmdspec = HS_CMDSPEC_CMD::wrap(cmd) | HS_CMDSPEC_SIZE::wrap((uint32_t) wordsize); return wordsize; } @@ -1932,7 +1932,7 @@ size_t CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) size_t ra_size = (msglen / sizeof(uint32_t)) + (msglen % sizeof(uint32_t) ? 1 : 0); // Store the CMD + SIZE in the next field - *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMREQ) | HS_CMDSPEC_SIZE::wrap(ra_size); + *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMREQ) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size); // Copy the key - do the endian inversion because another endian inversion // will be done for every control message before sending, and this KM message @@ -1984,7 +1984,7 @@ size_t CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t k keydata = reinterpret_cast(kmdata); } - *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMRSP) | HS_CMDSPEC_SIZE::wrap(ra_size); + *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMRSP) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size); HLOGC(cnlog.Debug, log << "createSrtHandshake: KMRSP: applying returned key length=" << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata, @@ -2230,7 +2230,7 @@ bool CUDT::createSrtHandshake( // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements. // WILL BE CHANGED HERE. ra_size = fillSrtHandshake((p + offset), total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); - *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); + *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size); HLOGC(cnlog.Debug, log << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size @@ -9286,7 +9286,7 @@ std::pair CUDT::packData(CPacket& w_packet) m_PacketFilter.packControlPacket(m_iSndCurrSeqNo, m_pCryptoControl->getSndCryptoFlags(), (w_packet))) { HLOGC(qslog.Debug, log << "filter: filter/CTL packet ready - packing instead of data."); - payload = w_packet.getLength(); + payload = (int) w_packet.getLength(); reason = "filter"; filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set @@ -9454,7 +9454,7 @@ std::pair CUDT::packData(CPacket& w_packet) // Encryption failed return std::make_pair(-1, enter_time); } - payload = w_packet.getLength(); /* Cipher may change length */ + payload = (int) w_packet.getLength(); /* Cipher may change length */ reason += " (encrypted)"; } @@ -9583,7 +9583,7 @@ void CUDT::sendLossReport(const std::vector > &loss_ if (!seqbuffer.empty()) { - sendCtrl(UMSG_LOSSREPORT, NULL, &seqbuffer[0], seqbuffer.size()); + sendCtrl(UMSG_LOSSREPORT, NULL, &seqbuffer[0], (int) seqbuffer.size()); } } @@ -9724,7 +9724,7 @@ int CUDT::processData(CUnit* in_unit) updateCC(TEV_RECEIVE, EventVariant(&packet)); ++m_iPktCount; - const int pktsz = packet.getLength(); + const int pktsz = (int) packet.getLength(); // Update time information // XXX Note that this adds the byte size of a packet // of which we don't yet know as to whether this has @@ -10250,7 +10250,7 @@ int CUDT::processData(CUnit* in_unit) } if (!lossdata.empty()) { - sendCtrl(UMSG_LOSSREPORT, NULL, &lossdata[0], lossdata.size()); + sendCtrl(UMSG_LOSSREPORT, NULL, &lossdata[0], (int) lossdata.size()); } // was_sent_in_order means either of: diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 36cb9b760..9e25087c5 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -302,7 +302,7 @@ void FECFilterBuiltin::ConfigureGroup(Group& g, int32_t seqno, size_t gstep, siz void FECFilterBuiltin::ResetGroup(Group& g) { - int32_t new_seq_base = CSeqNo::incseq(g.base, int(g.drop)); + const int32_t new_seq_base = CSeqNo::incseq(g.base, int(g.drop)); HLOGC(pflog.Debug, log << "FEC: ResetGroup (step=" << g.step << "): base %" << g.base << " -> %" << new_seq_base); @@ -386,6 +386,7 @@ void FECFilterBuiltin::feedSource(CPacket& packet) return; } + SRT_ASSERT(vert_off >= 0); int vert_pos = vert_off / int(sizeRow()); HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() @@ -2472,4 +2473,3 @@ size_t FECFilterBuiltin::ExtendColumns(size_t colgx) return colgx; } - diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 5b8bada83..4524d2232 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -254,7 +254,7 @@ void CPacket::setLength(size_t len) m_PacketVector[PV_DATA].setLength(len); } -void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size) +void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); diff --git a/srtcore/packet.h b/srtcore/packet.h index 1a43531b4..8724e9d07 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -247,7 +247,7 @@ friend class CRcvQueue; /// @param rparam [in] pointer to the second data structure, explained by the packet type. /// @param size [in] size of rparam, in number of bytes; - void pack(UDTMessageType pkttype, const int32_t* lparam = NULL, void* rparam = NULL, int size = 0); + void pack(UDTMessageType pkttype, const int32_t* lparam = NULL, void* rparam = NULL, size_t size = 0); /// Read the packet vector. /// @return Pointer to the packet vector. diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 56c53b2c0..615c8ce2c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1129,7 +1129,7 @@ CRcvQueue::CRcvQueue() , m_pHash(NULL) , m_pChannel(NULL) , m_pTimer(NULL) - , m_iPayloadSize() + , m_szPayloadSize() , m_bClosing(false) , m_LSLock() , m_pListener(NULL) @@ -1175,11 +1175,11 @@ CRcvQueue::~CRcvQueue() #endif -void CRcvQueue::init(int qsize, int payload, int version, int hsize, CChannel *cc, CTimer *t) +void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel *cc, CTimer *t) { - m_iPayloadSize = payload; + m_szPayloadSize = payload; - m_UnitQueue.init(qsize, payload, version); + m_UnitQueue.init(qsize, (int) payload, version); m_pHash = new CHash; m_pHash->init(hsize); @@ -1362,8 +1362,8 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad { // no space, skip this packet CPacket temp; - temp.m_pcData = new char[m_iPayloadSize]; - temp.setLength(m_iPayloadSize); + temp.m_pcData = new char[m_szPayloadSize]; + temp.setLength(m_szPayloadSize); THREAD_PAUSED(); EReadStatus rst = m_pChannel->recvfrom((w_addr), (temp)); THREAD_RESUMED(); @@ -1376,7 +1376,7 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad return rst == RST_ERROR ? RST_ERROR : RST_AGAIN; } - w_unit->m_Packet.setLength(m_iPayloadSize); + w_unit->m_Packet.setLength(m_szPayloadSize); // reading next incoming packet, recvfrom returns -1 is nothing has been received THREAD_PAUSED(); diff --git a/srtcore/queue.h b/srtcore/queue.h index 4cfe036b0..0ff8bb205 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -461,7 +461,7 @@ friend class CUDTUnited; /// @param [in] c UDP channel to be associated to the queue /// @param [in] t timer - void init(int size, int payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); + void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); /// Read a packet for a specific UDT socket id. /// @param [in] id Socket ID @@ -493,7 +493,7 @@ friend class CUDTUnited; CChannel* m_pChannel; // UDP channel for receving packets srt::sync::CTimer* m_pTimer; // shared timer with the snd queue - int m_iPayloadSize; // packet payload size + size_t m_szPayloadSize; // packet payload size volatile bool m_bClosing; // closing the worker #if ENABLE_LOGGING diff --git a/srtcore/window.h b/srtcore/window.h index 20b2374dc..b3baf0d60 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -310,7 +310,7 @@ class CPktTimeWindow: CPktTimeWindowTools // the ETH+IP+UDP+SRT header part elliminates the constant packet delivery time influence. // const size_t pktsz = pkt.getLength(); - m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? timediff_times_pl_size / pktsz : int(timediff); + m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? int(timediff_times_pl_size / pktsz) : int(timediff); // OLD CODE BEFORE BSTATS: // record the probing packets interval From 62d5d30b0d3d9056de4863cffcbc6fdeb67559d4 Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Wed, 6 Jan 2021 22:15:27 +0100 Subject: [PATCH 019/790] [core] fix dllexport of srt_rejectreason_str() Add missing SRT_API marker to the function's declaration. Fixes undefined reference when linking to libsrt on Windows. --- srtcore/srt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/srt.h b/srtcore/srt.h index f0a8b233a..5317720db 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -980,7 +980,7 @@ SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); SRT_API int srt_getrejectreason(SRTSOCKET sock); SRT_API int srt_setrejectreason(SRTSOCKET sock, int value); SRT_API extern const char* const srt_rejectreason_msg []; -const char* srt_rejectreason_str(int id); +SRT_API const char* srt_rejectreason_str(int id); SRT_API uint32_t srt_getversion(void); From adaf323157a393bd5f73033762e7848fc0ea6aa2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 11 Jan 2021 12:32:36 +0100 Subject: [PATCH 020/790] [build] Use Java 11 in Travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 151187e64..d369deb8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: cpp dist: trusty +jdk: + - oraclejdk11 addons: apt: From 26adb8bd43d096b81d68e20d3f31a3d227146204 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Jan 2021 17:33:02 +0100 Subject: [PATCH 021/790] [core] Refactoring backup link activation --- srtcore/group.cpp | 172 ++++++++++++++++++++++------------------------ srtcore/group.h | 37 +++++++++- 2 files changed, 120 insertions(+), 89 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index fd5b480ff..5b2131058 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3046,7 +3046,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t int& w_final_stat, set& w_sendable_pri, size_t& w_nsuccessful, - bool& w_is_nunstable) + bool& w_is_unstable) { bool none_succeeded = true; @@ -3118,7 +3118,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t else if (erc == SRT_EASYNCSND) { HLOGC(gslog.Debug, log << "grp/sendBackup: Link @" << w_u.m_SocketID << " DEEMED UNSTABLE (not ready to send)"); - w_is_nunstable = true; + w_is_unstable = true; } return none_succeeded; @@ -3175,19 +3175,72 @@ void CUDTGroup::sendBackup_Buffering(const char* buf, const int len, int32_t& w_ m_iLastSchedSeqNo = oldest_buffer_seq; } +bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, + const vector& unstable, + const vector& sendable, + const set sendable_pri, + string& activate_reason ATR_UNUSED) const +{ + SRT_ASSERT(sendable.size() >= unstable.size()); + bool need_activate = sendable.size() <= unstable.size(); // <= for sanity, should be just = + IF_HEAVY_LOGGING(activate_reason = "BY NO REASON???"); + if (need_activate) + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: all " << sendable.size() << " links unstable - will activate an idle link"); + IF_HEAVY_LOGGING(activate_reason = "no stable links"); + } + else if (sendable_pri.empty() || + (!idlers.empty() && FPriorityOrder::check(idlers[0]->weight, *sendable_pri.begin()))) + { + need_activate = true; +#if ENABLE_HEAVY_LOGGING + if (sendable_pri.empty()) + { + activate_reason = "no successful links found"; + LOGC(gslog.Debug, log << "grp/sendBackup: no active links were successful - will activate an idle link"); + } + else if (idlers.empty()) + { + // This should be impossible. + activate_reason = "WEIRD (no idle links!)"; + LOGC(gslog.Debug, + log << "grp/sendBackup: BY WEIRD AND IMPOSSIBLE REASON (IPE?) - will activate an idle link"); + } + else + { + // Only now we are granted that both sendable_pri and idlers are nonempty + LOGC(gslog.Debug, + log << "grp/sendBackup: found link pri " << idlers[0]->weight << " PREF OVER " + << (*sendable_pri.begin()) << " (highest from sendable) - will activate an idle link"); + activate_reason = "found higher pri link"; + } +#endif + } + else + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: sendable_pri (" << sendable_pri.size() << "): " << Printable(sendable_pri) + << " first idle pri: " << (idlers.size() > 0 ? int(idlers[0]->weight) : -1) + << " - will NOT activate an idle link"); + } + + return need_activate; +} + // [[using locked(this->m_GroupLock)]] -size_t CUDTGroup::sendBackup_CheckNeedActivate(const vector& idlers, - const char* buf, - const int len, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - int32_t& w_curseq, - int32_t& w_final_stat, - CUDTException& w_cx, - vector& w_sendstates, - vector& w_parallel, - vector& w_wipeme, - const string& activate_reason ATR_UNUSED) +size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& idlers, + const char* buf, + const int len, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + int32_t& w_curseq, + int32_t& w_final_stat, + CUDTException& w_cx, + vector& w_sendstates, + vector& w_parallel, + vector& w_wipeme, + const string& activate_reason ATR_UNUSED) { int stat = -1; @@ -3840,6 +3893,21 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // Sort the idle sockets by priority so the highest priority idle links are checked first. sort(idlers.begin(), idlers.end(), FPriorityOrder()); +#if ENABLE_HEAVY_LOGGING + { + vector show_running, show_idle; + for (vector::iterator i = sendable.begin(); i != sendable.end(); ++i) + show_running.push_back((*i)->id); + + for (vector::iterator i = idlers.begin(); i != idlers.end(); ++i) + show_idle.push_back((*i)->id); + + LOGC(gslog.Debug, + log << "grp/sendBackup: RUNNING: " << PrintableMod(show_running, "@") + << " IDLE: " << PrintableMod(show_idle, "@")); + } +#endif + vector sendstates; // Ok, we've separated the unstable from sendable just to know if: @@ -3856,21 +3924,6 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // priority should remain active. vector parallel; -#if ENABLE_HEAVY_LOGGING - { - vector show_running, show_idle; - for (vector::iterator i = sendable.begin(); i != sendable.end(); ++i) - show_running.push_back((*i)->id); - - for (vector::iterator i = idlers.begin(); i != idlers.end(); ++i) - show_idle.push_back((*i)->id); - - LOGC(gslog.Debug, - log << "grp/sendBackup: RUNNING: " << PrintableMod(show_running, "@") - << " IDLE: " << PrintableMod(show_idle, "@")); - } -#endif - int32_t curseq = SRT_SEQNO_NONE; size_t nsuccessful = 0; @@ -4003,65 +4056,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) sendBackup_Buffering(buf, len, (curseq), (w_mc)); - // CHECK: no sendable that exceeds unstable - // This embraces the case when there are no sendable at all. - // Note that unstable links still count as sendable; they - // are simply links that were qualified for sending, but: - // - have exceeded response timeout - // - have hit EASYNCSND error during sending - bool need_activate = sendable.size() <= unstable.size(); string activate_reason; - IF_HEAVY_LOGGING(activate_reason = "BY NO REASON???"); - if (need_activate) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: all " << sendable.size() << " links unstable - will activate an idle link"); - IF_HEAVY_LOGGING(activate_reason = "no stable links"); - } - else - { - // Another reason to activate might be if the link with highest priority - // among the idlers has a higher priority than any link currently active - // (those are collected in 'sendable_pri'). Check if there are any (if - // no sendable, a new link needs to be activated anyway), and if the - // priority has a lower number. - if (sendable_pri.empty() || (!idlers.empty() && FPriorityOrder::check(idlers[0]->weight, *sendable_pri.begin()))) - { - need_activate = true; -#if ENABLE_HEAVY_LOGGING - if (sendable_pri.empty()) - { - activate_reason = "no successful links found"; - LOGC(gslog.Debug, - log << "grp/sendBackup: no active links were successful - will activate an idle link"); - } - else if (idlers.empty()) - { - // This should be impossible. - activate_reason = "WEIRD (no idle links!)"; - LOGC(gslog.Debug, - log << "grp/sendBackup: BY WEIRD AND IMPOSSIBLE REASON (IPE?) - will activate an idle link"); - } - else - { - // Only now we are granted that both sendable_pri and idlers are nonempty - LOGC(gslog.Debug, - log << "grp/sendBackup: found link pri " << idlers[0]->weight << " PREF OVER " << (*sendable_pri.begin()) - << " (highest from sendable) - will activate an idle link"); - activate_reason = "found higher pri link"; - } -#endif - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: sendable_pri (" << sendable_pri.size() << "): " << Printable(sendable_pri) - << " first idle pri: " << (idlers.size() > 0 ? int(idlers[0]->weight) : -1) - << " - will NOT activate an idle link"); - } - } - - if (need_activate) + if (sendBackup_IsActivationNeeded(idlers, unstable, sendable, sendable_pri, activate_reason)) { if (idlers.empty()) { @@ -4069,7 +4065,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } else { - size_t n ATR_UNUSED = sendBackup_CheckNeedActivate(idlers, + const size_t n ATR_UNUSED = sendBackup_TryActivateIdleLink(idlers, buf, len, (none_succeeded), diff --git a/srtcore/group.h b/srtcore/group.h index 684e8ac0e..cfc9517e4 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -239,6 +239,21 @@ class CUDTGroup /// @retval false running link is unstable bool sendBackup_CheckRunningStability(const gli_t d, const time_point currtime); + /// Check link sending status + /// @param[in] d Group member iterator + /// @param[in] currtime Current time (logging only) + /// @param[in] stat Result of sending over the socket + /// @param[in] lastseq Last sent sequence number before the current sending operation + /// @param[in] pktseq Packet sequence number currently tried to be sent + /// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo) + /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) + /// @param[out] w_parallel Parallel link container (will be filled inside this function) + /// @param[out] w_final_stat Status to be reported by this function eventually + /// @param[out] w_sendable_pri Weight value from every link (will auto-sort) + /// @param[out] w_nsuccessful Updates the number of successful links + /// @param[out] w_is_unstable Set true if sending resulted in AGAIN error. + /// + /// @returns true if the sending operation result (submitted in stat) is a success, false otherwise. bool sendBackup_CheckSendStatus(const gli_t d, const time_point& currtime, const int stat, @@ -253,7 +268,27 @@ class CUDTGroup size_t& w_nsuccessful, bool& w_is_unstable); void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc); - size_t sendBackup_CheckNeedActivate(const std::vector& idlers, + + /// Check activation conditions and activate a backup link if needed. + /// Backup link activation is needed if: + /// + /// 1. All currently active links are unstable. + /// Note that unstable links still count as sendable; they + /// are simply links that were qualified for sending, but: + /// - have exceeded response timeout + /// - have hit EASYNCSND error during sending + /// + /// 2. Another reason to activate might be if one of idle links + /// has a higher weight than any link currently active + /// (those are collected in 'sendable_pri'). + /// If there are no sendable, a new link needs to be activated anyway. + bool sendBackup_IsActivationNeeded(const std::vector& idlers, + const std::vector& unstable, + const std::vector& sendable, + const std::set sendable_pri, + std::string& activate_reason) const; + + size_t sendBackup_TryActivateIdleLink(const std::vector& idlers, const char* buf, const int len, bool& w_none_succeeded, From dccaf765010de226531e0bd2d26c9acaef4040ea Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Jan 2021 11:54:14 +0100 Subject: [PATCH 022/790] [core] Fixed idle link activation by higher weight --- srtcore/group.cpp | 33 +++++++++++++++------------------ srtcore/group.h | 6 +++--- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 5b2131058..edc204d10 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3044,7 +3044,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t int32_t& w_curseq, vector& w_parallel, int& w_final_stat, - set& w_sendable_pri, + uint16_t& w_max_sendable_weight, size_t& w_nsuccessful, bool& w_is_unstable) { @@ -3113,7 +3113,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t none_succeeded = false; w_final_stat = stat; ++w_nsuccessful; - w_sendable_pri.insert(d->weight); + w_max_sendable_weight = max(w_max_sendable_weight, d->weight); } else if (erc == SRT_EASYNCSND) { @@ -3178,7 +3178,7 @@ void CUDTGroup::sendBackup_Buffering(const char* buf, const int len, int32_t& w_ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, const vector& unstable, const vector& sendable, - const set sendable_pri, + const uint16_t max_sendable_weight, string& activate_reason ATR_UNUSED) const { SRT_ASSERT(sendable.size() >= unstable.size()); @@ -3190,12 +3190,12 @@ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, log << "grp/sendBackup: all " << sendable.size() << " links unstable - will activate an idle link"); IF_HEAVY_LOGGING(activate_reason = "no stable links"); } - else if (sendable_pri.empty() || - (!idlers.empty() && FPriorityOrder::check(idlers[0]->weight, *sendable_pri.begin()))) + else if (sendable.empty() || + (!idlers.empty() && idlers[0]->weight > max_sendable_weight)) { need_activate = true; #if ENABLE_HEAVY_LOGGING - if (sendable_pri.empty()) + if (sendable.empty()) { activate_reason = "no successful links found"; LOGC(gslog.Debug, log << "grp/sendBackup: no active links were successful - will activate an idle link"); @@ -3209,19 +3209,18 @@ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, } else { - // Only now we are granted that both sendable_pri and idlers are nonempty LOGC(gslog.Debug, - log << "grp/sendBackup: found link pri " << idlers[0]->weight << " PREF OVER " - << (*sendable_pri.begin()) << " (highest from sendable) - will activate an idle link"); - activate_reason = "found higher pri link"; + log << "grp/sendBackup: found link weight " << idlers[0]->weight << " PREF OVER " + << max_sendable_weight << " (highest from sendable) - will activate an idle link"); + activate_reason = "found higher weight link"; } #endif } else { HLOGC(gslog.Debug, - log << "grp/sendBackup: sendable_pri (" << sendable_pri.size() << "): " << Printable(sendable_pri) - << " first idle pri: " << (idlers.size() > 0 ? int(idlers[0]->weight) : -1) + log << "grp/sendBackup: sendable max weight " << max_sendable_weight << ",: " + << " first idle wight: " << (idlers.size() > 0 ? int(idlers[0]->weight) : -1) << " - will NOT activate an idle link"); } @@ -3927,10 +3926,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) int32_t curseq = SRT_SEQNO_NONE; size_t nsuccessful = 0; - // Collect priorities from sendable links, added only after sending is successful. - // This will be used to check if any of the idlers have higher priority - // and therefore need to be activated. - set sendable_pri; + // Maximum weight of sendable links. + uint16_t max_sendable_weight = 0; // We believe that we need to send the payload over every sendable link anyway. for (vector::iterator snd = sendable.begin(); snd != sendable.end(); ++snd) @@ -3966,7 +3963,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) (curseq), (parallel), (final_stat), - (sendable_pri), + (max_sendable_weight), (nsuccessful), (is_unstable)); @@ -4057,7 +4054,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) sendBackup_Buffering(buf, len, (curseq), (w_mc)); string activate_reason; - if (sendBackup_IsActivationNeeded(idlers, unstable, sendable, sendable_pri, activate_reason)) + if (sendBackup_IsActivationNeeded(idlers, unstable, sendable, max_sendable_weight, activate_reason)) { if (idlers.empty()) { diff --git a/srtcore/group.h b/srtcore/group.h index cfc9517e4..9606c56e1 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -249,7 +249,7 @@ class CUDTGroup /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) /// @param[out] w_parallel Parallel link container (will be filled inside this function) /// @param[out] w_final_stat Status to be reported by this function eventually - /// @param[out] w_sendable_pri Weight value from every link (will auto-sort) + /// @param[out] w_max_sendable_weight Maximum weight value of sendable links /// @param[out] w_nsuccessful Updates the number of successful links /// @param[out] w_is_unstable Set true if sending resulted in AGAIN error. /// @@ -264,7 +264,7 @@ class CUDTGroup int32_t& w_curseq, std::vector& w_parallel, int& w_final_stat, - std::set& w_sendable_pri, + uint16_t& w_max_sendable_weight, size_t& w_nsuccessful, bool& w_is_unstable); void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc); @@ -285,7 +285,7 @@ class CUDTGroup bool sendBackup_IsActivationNeeded(const std::vector& idlers, const std::vector& unstable, const std::vector& sendable, - const std::set sendable_pri, + const uint16_t max_sendable_weight, std::string& activate_reason) const; size_t sendBackup_TryActivateIdleLink(const std::vector& idlers, From 6e78ecbe37baaab2f488a88f3d0ccdf155acb6a8 Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Fri, 6 Nov 2020 21:32:08 +0100 Subject: [PATCH 023/790] [core] Run the accept hook before opening caller's socket In listener mode, open the socket created for a connecting caller only after its accept hook has been run. This gives the hook an opportunity to set some 'pre' options (like SRTO_RCVBUF) on the socket before it gets passed to srt_accept(). --- srtcore/api.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 71fae7e61..fcbe80332 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -627,9 +627,6 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, m_Sockets[ns->m_SocketID] = ns; } - // bind to the same addr of listening socket - ns->m_pUDT->open(); - updateListenerMux(ns, ls); if (ls->m_pUDT->m_cbAcceptHook) { if (!ls->m_pUDT->runAcceptHook(ns->m_pUDT, peer.get(), w_hs, hspkt)) @@ -640,6 +637,11 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, goto ERR_ROLLBACK; } } + + // bind to the same addr of listening socket + ns->m_pUDT->open(); + updateListenerMux(ns, ls); + ns->m_pUDT->acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); } catch (...) From 28563056186ab7b583c739ce92410e97f21ddda6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 13 Jan 2021 18:26:24 +0100 Subject: [PATCH 024/790] [build] Made SRT versioned SO named with major and minor --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index baf1bf541..30dc33c3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -821,7 +821,7 @@ if (srt_libspec_shared) # shared libraries need PIC set (CMAKE_POSITION_INDEPENDENT_CODE ON) set_property(TARGET ${TARGET_srt}_shared PROPERTY OUTPUT_NAME ${TARGET_srt}) - set_target_properties (${TARGET_srt}_shared PROPERTIES VERSION ${SRT_VERSION} SOVERSION ${SRT_VERSION_MAJOR}) + set_target_properties (${TARGET_srt}_shared PROPERTIES VERSION ${SRT_VERSION} SOVERSION ${SRT_VERSION_MAJOR}.${SRT_VERSION_MINOR}) list (APPEND INSTALL_TARGETS ${TARGET_srt}_shared) if (ENABLE_ENCRYPTION) target_link_libraries(${TARGET_srt}_shared PRIVATE ${SSL_LIBRARIES}) From 3ca4e091af04713e811357c1c6e05ecd922edabc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 14 Jan 2021 11:18:08 +0100 Subject: [PATCH 025/790] [core] Main-backup: added QualifyMemberStates function (#1739) and moved corresponding code there (behavior untouched) --- srtcore/group.cpp | 139 +++++++++++++++++++++++++--------------------- srtcore/group.h | 15 +++++ 2 files changed, 90 insertions(+), 64 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index edc204d10..1f6378335 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2894,6 +2894,79 @@ bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vecto return true; } +void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, + vector& w_wipeme, + vector& w_idlers, + vector& w_pending, + vector& w_unstable, + vector& w_sendable) +{ + // First, check status of every link - no matter if idle or active. + for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) + { + if (d->sndstate != SRT_GST_BROKEN) + { + // Check the socket state prematurely in order not to uselessly + // send over a socket that is broken. + CUDT* const pu = (d->ps) + ? &d->ps->core() + : NULL; + + if (!pu || pu->m_bBroken) + { + HLOGC(gslog.Debug, log << "grp/sendBackup: socket @" << d->id << " detected +Broken - transit to BROKEN"); + d->sndstate = SRT_GST_BROKEN; + d->rcvstate = SRT_GST_BROKEN; + } + } + + // Check socket sndstate before sending + if (d->sndstate == SRT_GST_BROKEN) + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: socket in BROKEN state: @" << d->id + << ", sockstatus=" << SockStatusStr(d->ps ? d->ps->getStatus() : SRTS_NONEXIST)); + w_wipeme.push_back(d->id); + continue; + } + + if (d->sndstate == SRT_GST_IDLE) + { + if (!send_CheckIdle(d, (w_wipeme), (w_pending))) + continue; + + HLOGC(gslog.Debug, + log << "grp/sendBackup: socket in IDLE state: @" << d->id << " - will activate it IF NEEDED"); + // This is idle, we'll take care of them next time + // Might be that: + // - this socket is idle, while some NEXT socket is running + // - we need at least one running socket to work BEFORE activating the idle one. + // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, + // and all others will be activated using the ISN from the first one. + w_idlers.push_back(d); + sendBackup_CheckIdleTime(d); + continue; + } + + if (d->sndstate == SRT_GST_RUNNING) + { + if (!sendBackup_CheckRunningStability(d, (currtime))) + { + insert_uniq((w_unstable), d); + } + // Unstable links should still be used for sending. + w_sendable.push_back(d); + continue; + } + + HLOGC(gslog.Debug, + log << "grp/sendBackup: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" + << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); + + w_pending.push_back(d->id); + } +} + // [[using locked(this->m_GroupLock)]] void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) { @@ -3824,70 +3897,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) sendable.reserve(m_Group.size()); - // First, check status of every link - no matter if idle or active. - for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) - { - if (d->sndstate != SRT_GST_BROKEN) - { - // Check the socket state prematurely in order not to uselessly - // send over a socket that is broken. - CUDT* const pu = (d->ps) - ? &d->ps->core() - : NULL; - - if (!pu || pu->m_bBroken) - { - HLOGC(gslog.Debug, log << "grp/sendBackup: socket @" << d->id << " detected +Broken - transit to BROKEN"); - d->sndstate = SRT_GST_BROKEN; - d->rcvstate = SRT_GST_BROKEN; - } - } - - // Check socket sndstate before sending - if (d->sndstate == SRT_GST_BROKEN) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket in BROKEN state: @" << d->id - << ", sockstatus=" << SockStatusStr(d->ps ? d->ps->getStatus() : SRTS_NONEXIST)); - wipeme.push_back(d->id); - continue; - } - - if (d->sndstate == SRT_GST_IDLE) - { - if (!send_CheckIdle(d, (wipeme), (pending))) - continue; - - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket in IDLE state: @" << d->id << " - will activate it IF NEEDED"); - // This is idle, we'll take care of them next time - // Might be that: - // - this socket is idle, while some NEXT socket is running - // - we need at least one running socket to work BEFORE activating the idle one. - // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, - // and all others will be activated using the ISN from the first one. - idlers.push_back(d); - sendBackup_CheckIdleTime(d); - continue; - } - - if (d->sndstate == SRT_GST_RUNNING) - { - if (!sendBackup_CheckRunningStability(d, (currtime))) - { - insert_uniq((unstable), d); - } - // Unstable links should still be used for sending. - sendable.push_back(d); - continue; - } - - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" - << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); - - pending.push_back(d->id); - } + // Qualify states of member links + sendBackup_QualifyMemberStates(currtime, (wipeme), (idlers), (pending), (unstable), (sendable)); // Sort the idle sockets by priority so the highest priority idle links are checked first. sort(idlers.begin(), idlers.end(), FPriorityOrder()); diff --git a/srtcore/group.h b/srtcore/group.h index 9606c56e1..58eb76c8f 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -233,6 +233,21 @@ class CUDTGroup // Support functions for sendBackup and sendBroadcast bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pending); void sendBackup_CheckIdleTime(gli_t w_d); + + /// Qualify states of member links. + /// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]] + /// @param[in] currtime current timestamp + /// @param[out] w_wipeme broken links or links about to be closed + /// @param[out] w_idlers idle links + /// @param[out] w_pending links pending to be connected + /// @param[out] w_unstable member links qualified as unstable + /// @param[out] w_sendable all running member links, including unstable + void sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, + std::vector& w_wipeme, + std::vector& w_idlers, + std::vector& w_pending, + std::vector& w_unstable, + std::vector& w_sendable); /// Check if a running link is stable. /// @retval true running link is stable From a5609d39995b46e27dcc1222434da830bf669e16 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 14 Jan 2021 11:22:53 +0100 Subject: [PATCH 026/790] [core] Reimplemented strerror to use static messages (#1627) --- scripts/generate-error-types.tcl | 253 +++++++++++++++++++++++++++++++ srtcore/common.cpp | 226 +-------------------------- srtcore/common.h | 2 +- srtcore/filelist.maf | 1 + srtcore/strerror_defs.cpp | 165 ++++++++++++++++++++ 5 files changed, 427 insertions(+), 220 deletions(-) create mode 100755 scripts/generate-error-types.tcl create mode 100644 srtcore/strerror_defs.cpp diff --git a/scripts/generate-error-types.tcl b/scripts/generate-error-types.tcl new file mode 100755 index 000000000..15c8c0fa2 --- /dev/null +++ b/scripts/generate-error-types.tcl @@ -0,0 +1,253 @@ +#!/usr/bin/tclsh +#* +#* SRT - Secure, Reliable, Transport +#* Copyright (c) 2020 Haivision Systems Inc. +#* +#* This Source Code Form is subject to the terms of the Mozilla Public +#* License, v. 2.0. If a copy of the MPL was not distributed with this +#* file, You can obtain one at http://mozilla.org/MPL/2.0/. +#* +#*/ +# +#***************************************************************************** +#written by +# Haivision Systems Inc. +#***************************************************************************** + +set code_major { + UNKNOWN -1 + SUCCESS 0 + SETUP 1 + CONNECTION 2 + SYSTEMRES 3 + FILESYSTEM 4 + NOTSUP 5 + AGAIN 6 + PEERERROR 7 +} + +set code_minor { + NONE 0 + TIMEOUT 1 + REJECTED 2 + NORES 3 + SECURITY 4 + CLOSED 5 + + + CONNLOST 1 + NOCONN 2 + + THREAD 1 + MEMORY 2 + OBJECT 3 + + SEEKGFAIL 1 + READFAIL 2 + SEEKPFAIL 3 + WRITEFAIL 4 + + ISBOUND 1 + ISCONNECTED 2 + INVAL 3 + SIDINVAL 4 + ISUNBOUND 5 + NOLISTEN 6 + ISRENDEZVOUS 7 + ISRENDUNBOUND 8 + INVALMSGAPI 9 + INVALBUFFERAPI 10 + BUSY 11 + XSIZE 12 + EIDINVAL 13 + EEMPTY 14 + + WRAVAIL 1 + RDAVAIL 2 + XMTIMEOUT 3 + CONGESTION 4 +} + + +set errortypes { + + SUCCESS "Success" { + NONE "" + } + + SETUP "Connection setup failure" { + NONE "" + TIMEOUT "connection timed out" + REJECTED "connection rejected" + NORES "unable to create/configure SRT socket" + SECURITY "aborted for security reasons" + CLOSED "socket closed during operation" + } + + CONNECTION "" { + NONE "" + CONNLOST "Connection was broken" + NOCONN "Connection does not exist" + } + + SYSTEMRES "System resource failure" { + NONE "" + THREAD "unable to create new threads" + MEMORY "unable to allocate buffers" + OBJECT "unable to allocate a system object" + } + + FILESYSTEM "File system failure" { + NONE "" + SEEKGFAIL "cannot seek read position" + READFAIL "failure in read" + SEEKPFAIL "cannot seek write position" + WRITEFAIL "failure in write" + } + + NOTSUP "Operation not supported" { + NONE "" + ISBOUND "Cannot do this operation on a BOUND socket" + ISCONNECTED "Cannot do this operation on a CONNECTED socket" + INVAL "Bad parameters" + SIDINVAL "Invalid socket ID" + ISUNBOUND "Cannot do this operation on an UNBOUND socket" + NOLISTEN "Socket is not in listening state" + ISRENDEZVOUS "Listen/accept is not supported in rendezous connection setup" + ISRENDUNBOUND "Cannot call connect on UNBOUND socket in rendezvous connection setup" + INVALMSGAPI "Incorrect use of Message API (sendmsg/recvmsg)." + INVALBUFFERAPI "Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)." + BUSY "Another socket is already listening on the same port" + XSIZE "Message is too large to send (it must be less than the SRT send buffer size)" + EIDINVAL "Invalid epoll ID" + EEMPTY "All sockets removed from epoll, waiting would deadlock" + + } + + AGAIN "Non-blocking call failure" { + NONE "" + WRAVAIL "no buffer available for sending" + RDAVAIL "no data available for reading" + XMTIMEOUT "transmission timed out" + CONGESTION "early congestion notification" + } + + PEERERROR "The peer side has signaled an error" { + NONE "" + } + +} + +set main_array_item { +const char** strerror_array_major [] = { +$minor_array_list +}; + +} + +set major_size_item { +size_t strerror_array_sizes [] = { +$minor_array_sizes +}; +} + +set minor_array_item { +const char* strerror_msgs_$majorlc [] = { +$minor_message_items +}; + +} + +set strerror_function { + +const char* strerror_get_message(size_t major, size_t minor) +{ + static const char* const undefined = "UNDEFINED ERROR"; + + // Extract the major array + if (major >= sizeof(strerror_array_major)/sizeof(const char**)) + { + return undefined; + } + + const char** array = strerror_array_major[major]; + size_t size = strerror_array_sizes[major]; + + if (minor >= size) + { + return undefined; + } + + return array[minor]; +} + +} + +set globalheader { + /* + WARNING: Generated from ../scripts/generate-error-types.tcl + + DO NOT MODIFY. + + Copyright applies as per the generator script. + */ + +#include + +} + +proc Generate:imp {} { + + puts $::globalheader + + puts "namespace srt\n\{" + + # Generate major array + set majitem 0 + set minor_array_sizes "" + foreach {mt mm cont} $::errortypes { + + puts "// MJ_$mt '$mm'" + + # Generate minor array + set majorlc [string tolower $mt] + set minor_message_items "" + set minitem 0 + foreach {mnt mnm} $cont { + if {$mm == ""} { + set msg $mnm + } elseif {$mnm == ""} { + set msg $mm + } else { + set msg "$mm: $mnm" + } + append minor_message_items " \"$msg\", // MN_$mnt = $minitem\n" + incr minitem + } + append minor_message_items " \"\"" + puts [subst -nobackslashes -nocommands $::minor_array_item] + + append minor_array_list " strerror_msgs_$majorlc, // MJ_$mt = $majitem\n" + append minor_array_sizes " [expr {$minitem}],\n" + incr majitem + } + append minor_array_list " NULL\n" + append minor_array_sizes " 0\n" + + puts [subst -nobackslashes -nocommands $::main_array_item] + puts [subst -nobackslashes -nocommands $::major_size_item] + + puts "" + puts $::strerror_function + + puts "\} // namespace srt" +} + + +set defmode imp +if {[lindex $argv 0] != ""} { + set defmode [lindex $argv 0] +} + +Generate:$defmode + diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 1eff9d934..564bd9e35 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -88,230 +88,18 @@ m_iMinor(minor) m_iErrno = err; } +namespace srt { +const char* strerror_get_message(size_t major, size_t minor); +} + const char* CUDTException::getErrorMessage() const ATR_NOTHROW { - return getErrorString().c_str(); + return srt::strerror_get_message(m_iMajor, m_iMinor); } -const string& CUDTException::getErrorString() const +string CUDTException::getErrorString() const { - // translate "Major:Minor" code into text message. - - switch (m_iMajor) - { - case MJ_SUCCESS: - m_strMsg = "Success"; - break; - - case MJ_SETUP: - m_strMsg = "Connection setup failure"; - - switch (m_iMinor) - { - case MN_TIMEOUT: - m_strMsg += ": connection time out"; - break; - - case MN_REJECTED: - m_strMsg += ": connection rejected"; - break; - - case MN_NORES: - m_strMsg += ": unable to create/configure SRT socket"; - break; - - case MN_SECURITY: - m_strMsg += ": abort for security reasons"; - break; - - case MN_CLOSED: - m_strMsg += ": socket closed during operation"; - break; - - default: - break; - } - - break; - - case MJ_CONNECTION: - switch (m_iMinor) - { - case MN_CONNLOST: - m_strMsg = "Connection was broken"; - break; - - case MN_NOCONN: - m_strMsg = "Connection does not exist"; - break; - - default: - break; - } - - break; - - case MJ_SYSTEMRES: - m_strMsg = "System resource failure"; - - switch (m_iMinor) - { - case MN_THREAD: - m_strMsg += ": unable to create new threads"; - break; - - case MN_MEMORY: - m_strMsg += ": unable to allocate buffers"; - break; - - - case MN_OBJECT: - m_strMsg += ": unable to allocate system object"; - break; - - default: - break; - } - - break; - - case MJ_FILESYSTEM: - m_strMsg = "File system failure"; - - switch (m_iMinor) - { - case MN_SEEKGFAIL: - m_strMsg += ": cannot seek read position"; - break; - - case MN_READFAIL: - m_strMsg += ": failure in read"; - break; - - case MN_SEEKPFAIL: - m_strMsg += ": cannot seek write position"; - break; - - case MN_WRITEFAIL: - m_strMsg += ": failure in write"; - break; - - default: - break; - } - - break; - - case MJ_NOTSUP: - m_strMsg = "Operation not supported"; - - switch (m_iMinor) - { - case MN_ISBOUND: - m_strMsg += ": Cannot do this operation on a BOUND socket"; - break; - - case MN_ISCONNECTED: - m_strMsg += ": Cannot do this operation on a CONNECTED socket"; - break; - - case MN_INVAL: - m_strMsg += ": Bad parameters"; - break; - - case MN_SIDINVAL: - m_strMsg += ": Invalid socket ID"; - break; - - case MN_ISUNBOUND: - m_strMsg += ": Cannot do this operation on an UNBOUND socket"; - break; - - case MN_NOLISTEN: - m_strMsg += ": Socket is not in listening state"; - break; - - case MN_ISRENDEZVOUS: - m_strMsg += ": Listen/accept is not supported in rendezous connection setup"; - break; - - case MN_ISRENDUNBOUND: - m_strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup"; - break; - - case MN_INVALMSGAPI: - m_strMsg += ": Incorrect use of Message API (sendmsg/recvmsg)."; - break; - - case MN_INVALBUFFERAPI: - m_strMsg += ": Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)."; - break; - - case MN_BUSY: - m_strMsg += ": Another socket is already listening on the same port"; - break; - - case MN_XSIZE: - m_strMsg += ": Message is too large to send (it must be less than the SRT send buffer size)"; - break; - - case MN_EIDINVAL: - m_strMsg += ": Invalid epoll ID"; - break; - - case MN_EEMPTY: - m_strMsg += ": All sockets removed from epoll, waiting would deadlock"; - - default: - break; - } - - break; - - case MJ_AGAIN: - m_strMsg = "Non-blocking call failure"; - - switch (m_iMinor) - { - case MN_WRAVAIL: - m_strMsg += ": no buffer available for sending"; - break; - - case MN_RDAVAIL: - m_strMsg += ": no data available for reading"; - break; - - case MN_XMTIMEOUT: - m_strMsg += ": transmission timed out"; - break; - -#ifdef SRT_ENABLE_ECN - case MN_CONGESTION: - m_strMsg += ": early congestion notification"; - break; -#endif /* SRT_ENABLE_ECN */ - default: - break; - } - - break; - - case MJ_PEERERROR: - m_strMsg = "The peer side has signalled an error"; - - break; - - default: - m_strMsg = "Unknown error"; - } - - // Adding "errno" information - if ((MJ_SUCCESS != m_iMajor) && (0 < m_iErrno)) - { - m_strMsg += ": " + SysStrError(m_iErrno); - } - - return m_strMsg; + return getErrorMessage(); } #define UDT_XCODE(mj, mn) (int(mj)*1000)+int(mn) diff --git a/srtcore/common.h b/srtcore/common.h index 99005d1be..32be66179 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -110,7 +110,7 @@ class CUDTException: public std::exception return getErrorMessage(); } - const std::string& getErrorString() const; + std::string getErrorString() const; /// Get the system errno for the exception. /// @return errno. diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 74759f515..e6116cbb5 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -22,6 +22,7 @@ congctl.cpp srt_c_api.cpp window.cpp srt_compat.c +strerror_defs.cpp sync.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp new file mode 100644 index 000000000..ee32527bf --- /dev/null +++ b/srtcore/strerror_defs.cpp @@ -0,0 +1,165 @@ + + /* + WARNING: Generated from ../scripts/generate-error-types.tcl + + DO NOT MODIFY. + + Copyright applies as per the generator script. + */ + +#include + + +namespace srt +{ +// MJ_SUCCESS 'Success' + +const char* strerror_msgs_success [] = { + "Success", // MN_NONE = 0 + "" +}; + + +// MJ_SETUP 'Connection setup failure' + +const char* strerror_msgs_setup [] = { + "Connection setup failure", // MN_NONE = 0 + "Connection setup failure: connection timed out", // MN_TIMEOUT = 1 + "Connection setup failure: connection rejected", // MN_REJECTED = 2 + "Connection setup failure: unable to create/configure SRT socket", // MN_NORES = 3 + "Connection setup failure: aborted for security reasons", // MN_SECURITY = 4 + "Connection setup failure: socket closed during operation", // MN_CLOSED = 5 + "" +}; + + +// MJ_CONNECTION '' + +const char* strerror_msgs_connection [] = { + "", // MN_NONE = 0 + "Connection was broken", // MN_CONNLOST = 1 + "Connection does not exist", // MN_NOCONN = 2 + "" +}; + + +// MJ_SYSTEMRES 'System resource failure' + +const char* strerror_msgs_systemres [] = { + "System resource failure", // MN_NONE = 0 + "System resource failure: unable to create new threads", // MN_THREAD = 1 + "System resource failure: unable to allocate buffers", // MN_MEMORY = 2 + "System resource failure: unable to allocate a system object", // MN_OBJECT = 3 + "" +}; + + +// MJ_FILESYSTEM 'File system failure' + +const char* strerror_msgs_filesystem [] = { + "File system failure", // MN_NONE = 0 + "File system failure: cannot seek read position", // MN_SEEKGFAIL = 1 + "File system failure: failure in read", // MN_READFAIL = 2 + "File system failure: cannot seek write position", // MN_SEEKPFAIL = 3 + "File system failure: failure in write", // MN_WRITEFAIL = 4 + "" +}; + + +// MJ_NOTSUP 'Operation not supported' + +const char* strerror_msgs_notsup [] = { + "Operation not supported", // MN_NONE = 0 + "Operation not supported: Cannot do this operation on a BOUND socket", // MN_ISBOUND = 1 + "Operation not supported: Cannot do this operation on a CONNECTED socket", // MN_ISCONNECTED = 2 + "Operation not supported: Bad parameters", // MN_INVAL = 3 + "Operation not supported: Invalid socket ID", // MN_SIDINVAL = 4 + "Operation not supported: Cannot do this operation on an UNBOUND socket", // MN_ISUNBOUND = 5 + "Operation not supported: Socket is not in listening state", // MN_NOLISTEN = 6 + "Operation not supported: Listen/accept is not supported in rendezous connection setup", // MN_ISRENDEZVOUS = 7 + "Operation not supported: Cannot call connect on UNBOUND socket in rendezvous connection setup", // MN_ISRENDUNBOUND = 8 + "Operation not supported: Incorrect use of Message API (sendmsg/recvmsg).", // MN_INVALMSGAPI = 9 + "Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile).", // MN_INVALBUFFERAPI = 10 + "Operation not supported: Another socket is already listening on the same port", // MN_BUSY = 11 + "Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12 + "Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13 + "Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14 + "" +}; + + +// MJ_AGAIN 'Non-blocking call failure' + +const char* strerror_msgs_again [] = { + "Non-blocking call failure", // MN_NONE = 0 + "Non-blocking call failure: no buffer available for sending", // MN_WRAVAIL = 1 + "Non-blocking call failure: no data available for reading", // MN_RDAVAIL = 2 + "Non-blocking call failure: transmission timed out", // MN_XMTIMEOUT = 3 + "Non-blocking call failure: early congestion notification", // MN_CONGESTION = 4 + "" +}; + + +// MJ_PEERERROR 'The peer side has signaled an error' + +const char* strerror_msgs_peererror [] = { + "The peer side has signaled an error", // MN_NONE = 0 + "" +}; + + + +const char** strerror_array_major [] = { + strerror_msgs_success, // MJ_SUCCESS = 0 + strerror_msgs_setup, // MJ_SETUP = 1 + strerror_msgs_connection, // MJ_CONNECTION = 2 + strerror_msgs_systemres, // MJ_SYSTEMRES = 3 + strerror_msgs_filesystem, // MJ_FILESYSTEM = 4 + strerror_msgs_notsup, // MJ_NOTSUP = 5 + strerror_msgs_again, // MJ_AGAIN = 6 + strerror_msgs_peererror, // MJ_PEERERROR = 7 + NULL + +}; + + + +size_t strerror_array_sizes [] = { + 1, + 6, + 3, + 4, + 5, + 15, + 5, + 1, + 0 + +}; + + + + +const char* strerror_get_message(size_t major, size_t minor) +{ + static const char* const undefined = "UNDEFINED ERROR"; + + // Extract the major array + if (major >= sizeof(strerror_array_major)/sizeof(const char**)) + { + return undefined; + } + + const char** array = strerror_array_major[major]; + size_t size = strerror_array_sizes[major]; + + if (minor >= size) + { + return undefined; + } + + return array[minor]; +} + + +} // namespace srt From 648e8b5b266edaaa60f7a7178da21df1d1ca9aa2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 14 Jan 2021 15:42:21 +0100 Subject: [PATCH 027/790] [core] CSndLossList limits the maximum offset (#1733) and checks sequence numbers for validity Co-authored-by: Alex Pokotilo --- srtcore/list.cpp | 25 +++++++++++++++++++++++-- test/test_list.cpp | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 01e7e6016..c7ab7cfc8 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -111,7 +111,19 @@ void CSndLossList::traceState() const int CSndLossList::insert(int32_t seqno1, int32_t seqno2) { - SRT_ASSERT(CSeqNo::seqlen(seqno1, seqno2) > 0); + if (seqno1 < 0 || seqno2 < 0 ) { + LOGC(qslog.Error, log << "IPE: Tried to insert negative seqno " << seqno1 << ":" << seqno2 + << " into sender's loss list. Ignoring."); + return 0; + } + + const int inserted_range = CSeqNo::seqlen(seqno1, seqno2); + if (inserted_range <= 0 || inserted_range >= m_iSize) { + LOGC(qslog.Error, log << "IPE: Tried to insert too big range of seqno: " << inserted_range << ". Ignoring. " + << "seqno " << seqno1 << ":" << seqno2); + return 0; + } + ScopedLock listguard(m_ListLock); if (m_iLength == 0) @@ -123,7 +135,16 @@ int CSndLossList::insert(int32_t seqno1, int32_t seqno2) // Find the insert position in the non-empty list const int origlen = m_iLength; const int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1); - int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (offset >= m_iSize) + { + LOGC(qslog.Error, log << "IPE: New loss record is too far from the first record. Ignoring. " + << "First loss seqno " << m_caSeq[m_iHead].seqstart + << ", insert seqno " << seqno1 << ":" << seqno2); + return 0; + } + + int loc = (m_iHead + offset + m_iSize) % m_iSize; if (loc < 0) { diff --git a/test/test_list.cpp b/test/test_list.cpp index 72164a5e5..73e66c98b 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -59,6 +59,16 @@ TEST_F(CSndLossListTest, InsertPopOneElem) CheckEmptyArray(); } +TEST_F(CSndLossListTest, InsertNegativeSeqno) +{ + cerr << "Expecting IPE message:" << endl; + EXPECT_EQ(m_lossList->insert(1, SRT_SEQNO_NONE), 0); + EXPECT_EQ(m_lossList->insert(SRT_SEQNO_NONE, SRT_SEQNO_NONE), 0); + EXPECT_EQ(m_lossList->insert(SRT_SEQNO_NONE, 1), 0); + + CheckEmptyArray(); +} + /// Insert two elements at once and pop one by one TEST_F(CSndLossListTest, InsertPopTwoElemsRange) { @@ -505,13 +515,14 @@ TEST_F(CSndLossListTest, InsertFullListCoalesce) EXPECT_EQ(m_lossList->insert(i, i), 1); EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE); // Inserting additional element: 1 item more than list size. - // But given all elements coalesce into one entry, list size should still increase. - EXPECT_EQ(m_lossList->insert(CSndLossListTest::SIZE + 1, CSndLossListTest::SIZE + 1), 1); - EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE + 1); - for (int i = 1; i <= CSndLossListTest::SIZE + 1; i++) + // Given all elements coalesce into one entry, there is a place to insert it, + // but sequence span now exceeds list size. + EXPECT_EQ(m_lossList->insert(CSndLossListTest::SIZE + 1, CSndLossListTest::SIZE + 1), 0); + EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE); + for (int i = 1; i <= CSndLossListTest::SIZE; i++) { EXPECT_EQ(m_lossList->popLostSeq(), i); - EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE + 1 - i); + EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE - i); } EXPECT_EQ(m_lossList->popLostSeq(), -1); EXPECT_EQ(m_lossList->getLossLength(), 0); @@ -519,7 +530,7 @@ TEST_F(CSndLossListTest, InsertFullListCoalesce) CheckEmptyArray(); } -TEST_F(CSndLossListTest, DISABLED_InsertFullListNoCoalesce) +TEST_F(CSndLossListTest, InsertFullListNoCoalesce) { // We will insert each element with a gap of one elements. // This should lead to having space for only [i; SIZE] sequence numbers. @@ -530,23 +541,22 @@ TEST_F(CSndLossListTest, DISABLED_InsertFullListNoCoalesce) // [0]:taken, [1]: empty, [2]: taken, [3]: empty, ... EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE / 2); - // Inserting additional element: 1 item more than list size. - // There should be one free place for it at list[SIZE-1] - // right after previously inserted element. + // Inserting additional element out of the list span must fail. const int seqno1 = CSndLossListTest::SIZE + 2; - EXPECT_EQ(m_lossList->insert(seqno1, seqno1), 1); + EXPECT_EQ(m_lossList->insert(seqno1, seqno1), 0); - // Inserting one more element into a full list. - // There should be no place for it. - const int seqno2 = CSndLossListTest::SIZE + 4; - EXPECT_EQ(m_lossList->insert(seqno2, seqno2), 0); + // There should however be a place for one element right after the last inserted one. + const int seqno_last = CSndLossListTest::SIZE + 1; + EXPECT_EQ(m_lossList->insert(seqno_last, seqno_last), 1); - EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE + 1); - for (int i = 1; i <= CSndLossListTest::SIZE + 1; i++) + const int initial_length = m_lossList->getLossLength(); + EXPECT_EQ(initial_length, CSndLossListTest::SIZE / 2 + 1); + for (int i = 1; i <= CSndLossListTest::SIZE / 2; i++) { EXPECT_EQ(m_lossList->popLostSeq(), 2 * i); - EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE - i); + EXPECT_EQ(m_lossList->getLossLength(), initial_length - i); } + EXPECT_EQ(m_lossList->popLostSeq(), seqno_last); EXPECT_EQ(m_lossList->popLostSeq(), -1); EXPECT_EQ(m_lossList->getLossLength(), 0); From 8b4c8cdc797b4866c7229abd14bec7731471fd34 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 15 Jan 2021 10:04:35 +0100 Subject: [PATCH 028/790] [core][MAINT] Removed unused and swelling m_pAcceptSockets field + refax (#1740) --- srtcore/api.cpp | 75 ++++++++++--------------------------------------- srtcore/api.h | 5 +--- 2 files changed, 16 insertions(+), 64 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index fcbe80332..4acf933a6 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -100,9 +100,6 @@ CUDTSocket::~CUDTSocket() delete m_pUDT; m_pUDT = NULL; - delete m_pQueuedSockets; - delete m_pAcceptSockets; - releaseMutex(m_AcceptLock); releaseCond(m_AcceptCond); releaseMutex(m_ControlLock); @@ -162,7 +159,7 @@ bool CUDTSocket::readReady() return true; if (m_pUDT->m_bListening) { - return m_pQueuedSockets->size() > 0; + return m_QueuedSockets.size() > 0; } return broken(); @@ -518,8 +515,7 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, ns->setClosed(); ScopedLock acceptcg(ls->m_AcceptLock); - ls->m_pQueuedSockets->erase(ns->m_SocketID); - ls->m_pAcceptSockets->erase(ns->m_SocketID); + ls->m_QueuedSockets.erase(ns->m_SocketID); } else { @@ -551,7 +547,7 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, } // exceeding backlog, refuse the connection request - if (ls->m_pQueuedSockets->size() >= ls->m_uiBackLog) + if (ls->m_QueuedSockets.size() >= ls->m_uiBackLog) { w_error = SRT_REJ_BACKLOG; LOGC(cnlog.Note, log << "newConnection: listen backlog=" << ls->m_uiBackLog << " EXCEEDED"); @@ -778,7 +774,7 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, enterCS(ls->m_AcceptLock); try { - ls->m_pQueuedSockets->insert(ns->m_SocketID); + ls->m_QueuedSockets.insert(ns->m_SocketID); } catch (...) { @@ -806,10 +802,6 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, { HLOGC(cnlog.Debug, log << "ACCEPT: new socket @" << ns->m_SocketID << " NOT submitted to acceptance, another socket in the group is already connected"); - { - ScopedLock cg (ls->m_AcceptLock); - ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), ns->m_SocketID); - } // acknowledge INTERNAL users waiting for new connections on the listening socket // that are reported when a new socket is connected within an already connected group. @@ -1013,21 +1005,6 @@ int CUDTUnited::listen(const SRTSOCKET u, int backlog) s->m_uiBackLog = backlog; - try - { - s->m_pQueuedSockets = new set; - s->m_pAcceptSockets = new set; - } - catch (...) - { - delete s->m_pQueuedSockets; - delete s->m_pAcceptSockets; - - // XXX Translated std::bad_alloc into CUDTException specifying - // memory allocation failure... - throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); - } - // [[using assert(s->m_Status == OPENED)]]; // (still, unchanged) s->m_pUDT->setListenState(); // propagates CUDTException, @@ -1119,30 +1096,11 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ // This socket has been closed. accepted = true; } - else if (ls->m_pQueuedSockets->size() > 0) + else if (ls->m_QueuedSockets.size() > 0) { - // XXX REFACTORING REQUIRED HERE! - // Actually this should at best be something like that: - // set::iterator b = ls->m_pQueuedSockets->begin(); - // u = *b; - // ls->m_pQueuedSockets->erase(b); - // ls->m_pAcceptSockets->insert(u); - // - // It is also questionable why m_pQueuedSockets should be of type 'set'. - // There's no quick-searching capabilities of that container used anywhere except - // checkBrokenSockets and garbageCollect, which aren't performance-critical, - // whereas it's mainly used for getting the first element and iterating - // over elements, which is slow in case of std::set. It's also doubtful - // as to whether the sorting capability of std::set is properly used; - // the first is taken here, which is actually the socket with lowest - // possible descriptor value (as default operator< and ascending sorting - // used for std::set where SRTSOCKET=int). - // - // Consider using std::list or std::vector here. - - u = *(ls->m_pQueuedSockets->begin()); - ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); - ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + set::iterator b = ls->m_QueuedSockets.begin(); + u = *b; + ls->m_QueuedSockets.erase(b); accepted = true; } else if (!ls->m_pUDT->m_bSynRecving) @@ -1153,7 +1111,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ if (!accepted && (ls->m_Status == SRTS_LISTENING)) accept_sync.wait(); - if (ls->m_pQueuedSockets->empty()) + if (ls->m_QueuedSockets.empty()) m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_ACCEPT, false); } @@ -2343,7 +2301,7 @@ int CUDTUnited::selectEx( && s->m_pUDT->m_pRcvBuffer->isRcvDataReady() ) || (s->m_pUDT->m_bListening - && (s->m_pQueuedSockets->size() > 0))) + && (s->m_QueuedSockets.size() > 0))) { readfds->push_back(s->m_SocketID); ++ count; @@ -2696,8 +2654,7 @@ void CUDTUnited::checkBrokenSockets() } enterCS(ls->second->m_AcceptLock); - ls->second->m_pQueuedSockets->erase(s->m_SocketID); - ls->second->m_pAcceptSockets->erase(s->m_SocketID); + ls->second->m_QueuedSockets.erase(s->m_SocketID); leaveCS(ls->second->m_AcceptLock); } } @@ -2766,20 +2723,19 @@ void CUDTUnited::removeSocket(const SRTSOCKET u) // decrease multiplexer reference count, and remove it if necessary const int mid = s->m_iMuxID; - if (s->m_pQueuedSockets) { ScopedLock cg(s->m_AcceptLock); // if it is a listener, close all un-accepted sockets in its queue // and remove them later - for (set::iterator q = s->m_pQueuedSockets->begin(); - q != s->m_pQueuedSockets->end(); ++ q) + for (set::iterator q = s->m_QueuedSockets.begin(); + q != s->m_QueuedSockets.end(); ++ q) { sockets_t::iterator si = m_Sockets.find(*q); if (si == m_Sockets.end()) { // gone in the meantime - LOGC(smlog.Error, log << "removeSocket: IPE? socket @" << u + LOGC(smlog.Error, log << "removeSocket: IPE? socket @" << (*q) << " being queued for listener socket @" << s->m_SocketID << " is GONE in the meantime ???"); continue; @@ -3120,8 +3076,7 @@ void* CUDTUnited::garbageCollect(void* p) } enterCS(ls->second->m_AcceptLock); - ls->second->m_pQueuedSockets->erase(s->m_SocketID); - ls->second->m_pAcceptSockets->erase(s->m_SocketID); + ls->second->m_QueuedSockets.erase(s->m_SocketID); leaveCS(ls->second->m_AcceptLock); } self->m_Sockets.clear(); diff --git a/srtcore/api.h b/srtcore/api.h index fa18a6bba..1e98d7c49 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -88,8 +88,6 @@ class CUDTSocket #endif , m_iISN(0) , m_pUDT(NULL) - , m_pQueuedSockets(NULL) - , m_pAcceptSockets(NULL) , m_AcceptCond() , m_AcceptLock() , m_uiBackLog(0) @@ -127,8 +125,7 @@ class CUDTSocket CUDT* m_pUDT; //< pointer to the UDT entity - std::set* m_pQueuedSockets; //< set of connections waiting for accept() - std::set* m_pAcceptSockets; //< set of accept()ed connections + std::set m_QueuedSockets; //< set of connections waiting for accept() srt::sync::Condition m_AcceptCond; //< used to block "accept" call srt::sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond From 42a3bb789952dd0a8585739e3afb6f6c70523bd3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 15 Jan 2021 11:50:46 +0100 Subject: [PATCH 029/790] [tests] Enabled some CSndLossList::insert tests --- test/test_list.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/test_list.cpp b/test/test_list.cpp index 73e66c98b..bcb0074a3 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -563,7 +563,7 @@ TEST_F(CSndLossListTest, InsertFullListNoCoalesce) CheckEmptyArray(); } -TEST_F(CSndLossListTest, DISABLED_InsertFullListNegativeOffset) +TEST_F(CSndLossListTest, InsertFullListNegativeOffset) { for (int i = 10000000; i < 10000000 + CSndLossListTest::SIZE; i++) m_lossList->insert(i, i); @@ -581,6 +581,23 @@ TEST_F(CSndLossListTest, DISABLED_InsertFullListNegativeOffset) CheckEmptyArray(); } +TEST_F(CSndLossListTest, InsertPositiveOffsetTooFar) +{ + const int32_t head_seqno = 1000; + EXPECT_EQ(m_lossList->insert(head_seqno, head_seqno), 1); + EXPECT_EQ(m_lossList->getLossLength(), 1); + + // The offset of the sequence number being added does not fit + // into the size of the loss list, it must be ignored. + // Normally this situation should not happen. + + const int32_t outofbound_seqno = head_seqno + CSndLossListTest::SIZE; + m_lossList->insert(outofbound_seqno, outofbound_seqno); + + const int32_t outofbound_seqno2 = head_seqno + 2 * CSndLossListTest::SIZE; + m_lossList->insert(outofbound_seqno2, outofbound_seqno2); +} + ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// TEST_F(CSndLossListTest, InsertNoUpdateElement01) From c6591d53e6097c42d23d0054644c07dea0f02788 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 18 Jan 2021 11:06:33 +0100 Subject: [PATCH 030/790] [build] Fixed PowerShell script to not fail on CMake warning. CMake complaints about CMake version 2.8.8 requirement of GTest. Versions below 2.8.12 will soon become deprecated. Co-authored-by: Maxim Sharabayko --- scripts/build-windows.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index c651a77a9..47ccb97f5 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -134,6 +134,11 @@ if ( $VS_VERSION -eq '2019' ) { # fire cmake to build project files $execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags" Write-Output $execVar + +# Reset reaction to Continue for cmake as it sometimes tends to print +# things on stderr, which is understood by PowerShell as error. The +# exit code from cmake will be checked anyway. +$ErrorActionPreference = "Continue" Invoke-Expression "& $execVar" # check build ran OK, exit if cmake failed @@ -141,6 +146,8 @@ if( $LASTEXITCODE -ne 0 ) { return $LASTEXITCODE } +$ErrorActionPreference = "Stop" + # run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL . $PSScriptRoot/set-version-metadata.ps1 From b665e3588098c2d23bd1dafd02822c4f4b36c05a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 19 Jan 2021 16:25:16 +0100 Subject: [PATCH 031/790] [core] Main-backup: renaming member link arrays (#1744) - vector idlers -> idleLinks - vector pending -> pendingSockets - vector sendable -> activeLinks - vector sendstates not used in the backup mode, removed. - vector unstable - unstableLinks - uint16_t max_sendable_weight -> maxActiveWeight --- srtcore/group.cpp | 279 ++++++++++++++++++++++------------------------ srtcore/group.h | 35 +++--- 2 files changed, 150 insertions(+), 164 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 1f6378335..c664094aa 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1115,8 +1115,8 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // for which NO ITERATORS ARE INVALIDATED after a node at particular // iterator has been removed, except for that iterator itself. vector wipeme; - vector idlers; - vector pending; // need sock ids as it will be checked out of lock + vector idleLinks; + vector pendingSockets; // need sock ids as it will be checked out of lock int32_t curseq = SRT_SEQNO_NONE; @@ -1125,7 +1125,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) int stat = 0; SRT_ATR_UNUSED CUDTException cx(MJ_SUCCESS, MN_NONE, 0); - vector sendable; + vector activeLinks; // First, acquire GlobControlLock to make sure all member sockets still exist enterCS(m_pGlobal->m_GlobControlLock); @@ -1200,7 +1200,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { HLOGC(gslog.Debug, log << "CUDTGroup::send. @" << d->id << " is still " << SockStatusStr(st) << ", skipping."); - pending.push_back(d->id); + pendingSockets.push_back(d->id); continue; } @@ -1211,7 +1211,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // - we need at least one running socket to work BEFORE activating the idle one. // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, // and all others will be activated using the ISN from the first one. - idlers.push_back(d); + idleLinks.push_back(d); continue; } @@ -1219,7 +1219,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { HLOGC(gslog.Debug, log << "grp/sendBroadcast: socket in RUNNING state: @" << d->id << " - will send a payload"); - sendable.push_back(d); + activeLinks.push_back(d); continue; } @@ -1227,12 +1227,12 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) log << "grp/sendBroadcast: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); - pending.push_back(d->id); + pendingSockets.push_back(d->id); } vector sendstates; - for (vector::iterator snd = sendable.begin(); snd != sendable.end(); ++snd) + for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) { gli_t d = *snd; int erc = 0; // success @@ -1303,8 +1303,8 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Now we can go to the idle links and attempt to send the payload // also over them. - // { sendBroadcast_ActivateIdlers - for (vector::iterator i = idlers.begin(); i != idlers.end(); ++i) + // TODO: { sendBroadcast_ActivateIdleLinks + for (vector::iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) { gli_t d = *i; if (!d->ps->m_GroupOf) @@ -1369,7 +1369,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // { send_CheckBrokenSockets() - if (!pending.empty()) + if (!pendingSockets.empty()) { HLOGC(gslog.Debug, log << "grp/sendBroadcast: found pending sockets, polling them."); @@ -1382,7 +1382,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/sendBroadcast: IPE: reported pending sockets, but EID is empty - wiping pending!"); - copy(pending.begin(), pending.end(), back_inserter(wipeme)); + copy(pendingSockets.begin(), pendingSockets.end(), back_inserter(wipeme)); } else { @@ -1404,7 +1404,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) HLOGC(gslog.Debug, log << "grp/sendBroadcast: RDY: " << DisplayEpollResults(sready)); // sockets in EX: should be moved to wipeme. - for (vector::iterator i = pending.begin(); i != pending.end(); ++i) + for (vector::iterator i = pendingSockets.begin(); i != pendingSockets.end(); ++i) { if (CEPoll::isready(sready, *i, SRT_EPOLL_ERR)) { @@ -1447,10 +1447,9 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Links that were successful, have the len value in state. // First thing then, find out if at least one link was successful. - // This might even be one of the idlers only, this doesn't matter. - // If there were any running links successful, they have set the sequence. - // If there were only some reactivated idlers successful, the first - // idler has defined the sequence. + // The first successful link sets the sequence value, + // the following links derive it. This might be also the first idle + // link with its random-generated ISN, if there were no active links. vector successful, blocked; @@ -1613,7 +1612,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } else { - sendable.clear(); + activeLinks.clear(); sendstates.clear(); // Extract gli's from the whole group that have id found in the array. @@ -1631,10 +1630,10 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) dd->sndstate = SRT_GST_BROKEN; } else if (rdev & SRT_EPOLL_OUT) - sendable.push_back(dd); + activeLinks.push_back(dd); } - for (vector::iterator snd = sendable.begin(); snd != sendable.end(); ++snd) + for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) { gli_t d = *snd; @@ -2869,7 +2868,7 @@ struct FPriorityOrder }; // [[using maybe_locked(this->m_GroupLock)]] -bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vector& w_pending) +bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vector& w_pendingSockets) { SRT_SOCKSTATUS st = SRTS_NONEXIST; if (d->ps) @@ -2887,7 +2886,7 @@ bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vecto if (st != SRTS_CONNECTED) { HLOGC(gslog.Debug, log << "CUDTGroup::send. @" << d->id << " is still " << SockStatusStr(st) << ", skipping."); - w_pending.push_back(d->id); + w_pendingSockets.push_back(d->id); return false; } @@ -2896,10 +2895,10 @@ bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vecto void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, vector& w_wipeme, - vector& w_idlers, - vector& w_pending, - vector& w_unstable, - vector& w_sendable) + vector& w_idleLinks, + vector& w_pendingSockets, + vector& w_unstableLinks, + vector& w_activeLinks) { // First, check status of every link - no matter if idle or active. for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) @@ -2932,7 +2931,7 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c if (d->sndstate == SRT_GST_IDLE) { - if (!send_CheckIdle(d, (w_wipeme), (w_pending))) + if (!send_CheckIdle(d, (w_wipeme), (w_pendingSockets))) continue; HLOGC(gslog.Debug, @@ -2943,7 +2942,7 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c // - we need at least one running socket to work BEFORE activating the idle one. // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, // and all others will be activated using the ISN from the first one. - w_idlers.push_back(d); + w_idleLinks.push_back(d); sendBackup_CheckIdleTime(d); continue; } @@ -2952,10 +2951,10 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c { if (!sendBackup_CheckRunningStability(d, (currtime))) { - insert_uniq((w_unstable), d); + insert_uniq((w_unstableLinks), d); } // Unstable links should still be used for sending. - w_sendable.push_back(d); + w_activeLinks.push_back(d); continue; } @@ -2963,7 +2962,7 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c log << "grp/sendBackup: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); - w_pending.push_back(d->id); + w_pendingSockets.push_back(d->id); } } @@ -3117,7 +3116,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t int32_t& w_curseq, vector& w_parallel, int& w_final_stat, - uint16_t& w_max_sendable_weight, + uint16_t& w_maxActiveWeight, size_t& w_nsuccessful, bool& w_is_unstable) { @@ -3186,7 +3185,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t none_succeeded = false; w_final_stat = stat; ++w_nsuccessful; - w_max_sendable_weight = max(w_max_sendable_weight, d->weight); + w_maxActiveWeight = max(w_maxActiveWeight, d->weight); } else if (erc == SRT_EASYNCSND) { @@ -3248,32 +3247,32 @@ void CUDTGroup::sendBackup_Buffering(const char* buf, const int len, int32_t& w_ m_iLastSchedSeqNo = oldest_buffer_seq; } -bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, - const vector& unstable, - const vector& sendable, - const uint16_t max_sendable_weight, +bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idleLinks, + const vector& unstableLinks, + const vector& activeLinks, + const uint16_t maxActiveWeight, string& activate_reason ATR_UNUSED) const { - SRT_ASSERT(sendable.size() >= unstable.size()); - bool need_activate = sendable.size() <= unstable.size(); // <= for sanity, should be just = + SRT_ASSERT(activeLinks.size() >= unstableLinks.size()); + bool need_activate = activeLinks.size() <= unstableLinks.size(); // <= for sanity, should be just == IF_HEAVY_LOGGING(activate_reason = "BY NO REASON???"); if (need_activate) { HLOGC(gslog.Debug, - log << "grp/sendBackup: all " << sendable.size() << " links unstable - will activate an idle link"); + log << "grp/sendBackup: all " << activeLinks.size() << " links unstable - will activate an idle link"); IF_HEAVY_LOGGING(activate_reason = "no stable links"); } - else if (sendable.empty() || - (!idlers.empty() && idlers[0]->weight > max_sendable_weight)) + else if (activeLinks.empty() || + (!idleLinks.empty() && idleLinks[0]->weight > maxActiveWeight)) { need_activate = true; #if ENABLE_HEAVY_LOGGING - if (sendable.empty()) + if (activeLinks.empty()) { activate_reason = "no successful links found"; LOGC(gslog.Debug, log << "grp/sendBackup: no active links were successful - will activate an idle link"); } - else if (idlers.empty()) + else if (idleLinks.empty()) { // This should be impossible. activate_reason = "WEIRD (no idle links!)"; @@ -3283,8 +3282,8 @@ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, else { LOGC(gslog.Debug, - log << "grp/sendBackup: found link weight " << idlers[0]->weight << " PREF OVER " - << max_sendable_weight << " (highest from sendable) - will activate an idle link"); + log << "grp/sendBackup: found link weight " << idleLinks[0]->weight << " PREF OVER " + << maxActiveWeight << " (highest from active links) - will activate an idle link"); activate_reason = "found higher weight link"; } #endif @@ -3292,8 +3291,8 @@ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, else { HLOGC(gslog.Debug, - log << "grp/sendBackup: sendable max weight " << max_sendable_weight << ",: " - << " first idle wight: " << (idlers.size() > 0 ? int(idlers[0]->weight) : -1) + log << "grp/sendBackup: max active weight " << maxActiveWeight << ",: " + << " first idle wight: " << (idleLinks.size() > 0 ? int(idleLinks[0]->weight) : -1) << " - will NOT activate an idle link"); } @@ -3301,7 +3300,7 @@ bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idlers, } // [[using locked(this->m_GroupLock)]] -size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& idlers, +size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& idleLinks, const char* buf, const int len, bool& w_none_succeeded, @@ -3309,7 +3308,6 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i int32_t& w_curseq, int32_t& w_final_stat, CUDTException& w_cx, - vector& w_sendstates, vector& w_parallel, vector& w_wipeme, const string& activate_reason ATR_UNUSED) @@ -3319,12 +3317,12 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i // If we have no stable links, activate one of idle links. HLOGC(gslog.Debug, - log << "grp/sendBackup: " << activate_reason << ", trying to activate an idle link (" << idlers.size() + log << "grp/sendBackup: " << activate_reason << ", trying to activate an idle link (" << idleLinks.size() << " available)"); size_t nactive = 0; - for (vector::const_iterator i = idlers.begin(); i != idlers.end(); ++i) + for (vector::const_iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) { int erc = 0; gli_t d = *i; @@ -3333,6 +3331,8 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i try { + // TODO: At this point all packets that could be sent + // are located in m_SenderBuffer. So maybe just use sendBackupRexmit()? if (w_curseq == SRT_SEQNO_NONE) { // This marks the fact that the given here packet @@ -3375,9 +3375,6 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i d->sndresult = stat; d->laststatus = d->ps->getStatus(); - const Sendstate cstate = {d->id, &*d, stat, erc}; - w_sendstates.push_back(cstate); - if (stat != -1) { if (d->sndstate != SRT_GST_RUNNING) @@ -3434,73 +3431,63 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::send_CheckPendingSockets(const vector& pending, vector& w_wipeme) +void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets, vector& w_wipeme) { - // If we have at least one stable link, then select a link that have the - // highest priority and silence the rest. - - // Note: If we have one stable link, this is the situation we need. - // If we have no stable links at all, there's nothing we can do anyway. - // The freshly activated previously idle links don't count because we - // just started them and we can't determine their stability. At least if - // we have one link that is stable and the freshly activated link is actually - // stable too, we'll check this next time. - // - if (!pending.empty()) - { - HLOGC(gslog.Debug, log << "grp/send*: found pending sockets, polling them."); + if (pendingSockets.empty()) + return; - // These sockets if they are in pending state, they should be added to m_SndEID - // at the connecting stage. - CEPoll::fmap_t sready; + HLOGC(gslog.Debug, log << "grp/send*: found pending sockets, polling them."); + + // These sockets if they are in pending state, they should be added to m_SndEID + // at the connecting stage. + CEPoll::fmap_t sready; + if (m_SndEpolld->watch_empty()) + { + // Sanity check - weird pending reported. + LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); + copy(pendingSockets.begin(), pendingSockets.end(), back_inserter(w_wipeme)); + } + else + { + // Some sockets could have been closed in the meantime. if (m_SndEpolld->watch_empty()) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + { - // Sanity check - weird pending reported. - LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); - copy(pending.begin(), pending.end(), back_inserter(w_wipeme)); + InvertedLock ug(m_GroupLock); + m_pGlobal->m_EPoll.swait( + *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened } - else - { - // Some sockets could have been closed in the meantime. - if (m_SndEpolld->watch_empty()) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - { - InvertedLock ug(m_GroupLock); - m_pGlobal->m_EPoll.swait( - *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened - } - if (m_bClosing) - { - HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } + if (m_bClosing) + { + HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } - HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); + HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); - // sockets in EX: should be moved to w_wipeme. - for (vector::const_iterator i = pending.begin(); i != pending.end(); ++i) + // sockets in EX: should be moved to w_wipeme. + for (vector::const_iterator i = pendingSockets.begin(); i != pendingSockets.end(); ++i) + { + if (CEPoll::isready(sready, *i, SRT_EPOLL_ERR)) { - if (CEPoll::isready(sready, *i, SRT_EPOLL_ERR)) - { - HLOGC(gslog.Debug, log << "grp/send*: Socket @" << (*i) << " reported FAILURE - moved to wiped."); - // Failed socket. Move d to w_wipeme. Remove from eid. - w_wipeme.push_back(*i); - int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); - } + HLOGC(gslog.Debug, log << "grp/send*: Socket @" << (*i) << " reported FAILURE - moved to wiped."); + // Failed socket. Move d to w_wipeme. Remove from eid. + w_wipeme.push_back(*i); + int no_events = 0; + m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); } - - // After that, all sockets that have been reported - // as ready to write should be removed from EID. This - // will also remove those sockets that have been added - // as redundant links at the connecting stage and became - // writable (connected) before this function had a chance - // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); } + + // After that, all sockets that have been reported + // as ready to write should be removed from EID. This + // will also remove those sockets that have been added + // as redundant links at the connecting stage and became + // writable (connected) before this function had a chance + // to check them. + m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); } } @@ -3553,7 +3540,7 @@ struct FByOldestActive }; // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, +void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstableLinks, vector& w_parallel, int& w_final_stat, bool& w_none_succeeded, @@ -3577,7 +3564,7 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, // Potential problem to be checked in developer mode for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) { - if (std::find(unstable.begin(), unstable.end(), *p) != unstable.end()) + if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) { LOGC(gslog.Debug, log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); @@ -3589,10 +3576,10 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, // over any link (hence "none succeeded"), but there are some unstable // links and no parallel links. We need to WAIT for any of the links // to become available for sending. - if (w_parallel.empty() && !unstable.empty() && w_none_succeeded) + if (w_parallel.empty() && !unstableLinks.empty() && w_none_succeeded) { HLOGC(gslog.Debug, log << "grp/sendBackup: no parallel links and " - << unstable.size() << " unstable links - checking..."); + << unstableLinks.size() << " unstable links - checking..."); // Note: GroupLock is set already, skip locks and checks getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); @@ -3637,7 +3624,7 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, // Some sockets could have been closed in the meantime. if (m_SndEpolld->watch_empty()) { - HLOGC(gslog.Debug, log << "grp/sendBackup: no more sendable sockets - group broken"); + HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -3776,7 +3763,7 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, } HLOGC(gslog.Debug, log << "grp/sendBackup: " << nactivated << " links activated with " - << unstable.size() << " unstable"); + << unstableLinks.size() << " unstable"); } // The most important principle is to keep the data being sent constantly, @@ -3849,13 +3836,12 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstable, int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) { - // Avoid stupid errors in the beginning. if (len <= 0) { throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - // Live only - sorry. + // Only live streaming is supported if (len > SRT_LIVE_MAX_PLSIZE) { LOGC(gslog.Error, log << "grp/send(backup): buffer size=" << len << " exceeds maximum allowed in live mode"); @@ -3870,10 +3856,10 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // for which NO ITERATORS ARE INVALIDATED after a node at particular // iterator has been removed, except for that iterator itself. vector wipeme; - vector idlers; - vector pending; - vector unstable; - vector sendable; + vector idleLinks; + vector pendingSockets; + vector activeLinks; // All non-idle and non-pending links + vector unstableLinks; // Active, but unstable. int stat = 0; int final_stat = -1; @@ -3895,21 +3881,21 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) steady_clock::time_point currtime = steady_clock::now(); - sendable.reserve(m_Group.size()); + activeLinks.reserve(m_Group.size()); // Qualify states of member links - sendBackup_QualifyMemberStates(currtime, (wipeme), (idlers), (pending), (unstable), (sendable)); + sendBackup_QualifyMemberStates(currtime, (wipeme), (idleLinks), (pendingSockets), (unstableLinks), (activeLinks)); // Sort the idle sockets by priority so the highest priority idle links are checked first. - sort(idlers.begin(), idlers.end(), FPriorityOrder()); + sort(idleLinks.begin(), idleLinks.end(), FPriorityOrder()); #if ENABLE_HEAVY_LOGGING { vector show_running, show_idle; - for (vector::iterator i = sendable.begin(); i != sendable.end(); ++i) + for (vector::iterator i = activeLinks.begin(); i != activeLinks.end(); ++i) show_running.push_back((*i)->id); - for (vector::iterator i = idlers.begin(); i != idlers.end(); ++i) + for (vector::iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) show_idle.push_back((*i)->id); LOGC(gslog.Debug, @@ -3918,11 +3904,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } #endif - vector sendstates; - - // Ok, we've separated the unstable from sendable just to know if: - // - we have any STABLE sendable (if not, we must activate a backup link) - // - we have multiple stable sendable and we need to stop all but one + // Ok, we've separated the unstable from activeLinks just to know if: + // - we have any STABLE activeLinks (if not, we must activate a backup link) + // - we have multiple stable activeLinks and we need to stop all but one // Normally there should be only one link with state == SRT_GST_RUNNING, but there might // be multiple links set as running when a "breaking suspection" is set on a link. @@ -3937,11 +3921,11 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) int32_t curseq = SRT_SEQNO_NONE; size_t nsuccessful = 0; - // Maximum weight of sendable links. - uint16_t max_sendable_weight = 0; + // Maximum weight of active links. + uint16_t maxActiveWeight = 0; - // We believe that we need to send the payload over every sendable link anyway. - for (vector::iterator snd = sendable.begin(); snd != sendable.end(); ++snd) + // We believe that we need to send the payload over every activeLinks link anyway. + for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) { gli_t d = *snd; int erc = 0; // success @@ -3974,15 +3958,15 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) (curseq), (parallel), (final_stat), - (max_sendable_weight), + (maxActiveWeight), (nsuccessful), (is_unstable)); + // TODO: Wasn't it done in sendBackup_QualifyMemberStates()? Sanity check? if (is_unstable && is_zero(u.m_tsUnstableSince)) // Add to unstable only if it wasn't unstable already - insert_uniq((unstable), d); + insert_uniq((unstableLinks), d); const Sendstate cstate = {d->id, &*d, stat, erc}; - sendstates.push_back(cstate); d->sndresult = stat; d->laststatus = d->ps->getStatus(); } @@ -4065,15 +4049,15 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) sendBackup_Buffering(buf, len, (curseq), (w_mc)); string activate_reason; - if (sendBackup_IsActivationNeeded(idlers, unstable, sendable, max_sendable_weight, activate_reason)) + if (sendBackup_IsActivationNeeded(idleLinks, unstableLinks, activeLinks, maxActiveWeight, activate_reason)) { - if (idlers.empty()) + if (idleLinks.empty()) { - HLOGP(gslog.Debug, "grp/sendBackup: no idlers to activate, keeping only unstable links"); + HLOGP(gslog.Debug, "grp/sendBackup: no idle links to activate, keeping only unstable links"); } else { - const size_t n ATR_UNUSED = sendBackup_TryActivateIdleLink(idlers, + const size_t n ATR_UNUSED = sendBackup_TryActivateIdleLink(idleLinks, buf, len, (none_succeeded), @@ -4081,7 +4065,6 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) (curseq), (final_stat), (cx), - (sendstates), (parallel), (wipeme), activate_reason); @@ -4092,11 +4075,11 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) else { HLOGC(gslog.Debug, - log << "grp/sendBackup: have sendable links, stable=" << (sendable.size() - unstable.size()) - << " unstable=" << unstable.size()); + log << "grp/sendBackup: have activeLinks links, stable=" << (activeLinks.size() - unstableLinks.size()) + << " unstable=" << unstableLinks.size()); } - send_CheckPendingSockets(pending, (wipeme)); + send_CheckPendingSockets(pendingSockets, (wipeme)); // Re-check after the waiting lock has been reacquired if (m_bClosing) @@ -4108,7 +4091,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - sendBackup_CheckParallelLinks(unstable, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); + sendBackup_CheckParallelLinks(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); // (closing condition checked inside this call) if (none_succeeded) diff --git a/srtcore/group.h b/srtcore/group.h index 58eb76c8f..2196dd4ce 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -231,23 +231,23 @@ class CUDTGroup int sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc); // Support functions for sendBackup and sendBroadcast - bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pending); + bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pendingLinks); void sendBackup_CheckIdleTime(gli_t w_d); /// Qualify states of member links. /// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]] - /// @param[in] currtime current timestamp - /// @param[out] w_wipeme broken links or links about to be closed - /// @param[out] w_idlers idle links - /// @param[out] w_pending links pending to be connected - /// @param[out] w_unstable member links qualified as unstable - /// @param[out] w_sendable all running member links, including unstable + /// @param[in] currtime current timestamp + /// @param[out] w_wipeme broken links or links about to be closed + /// @param[out] w_idleLinks idle links (connected, but not used for transmission) + /// @param[out] w_pendingSockets sockets pending to be connected + /// @param[out] w_unstableLinks active member links qualified as unstable + /// @param[out] w_activeLinks all active member links, including unstable void sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, std::vector& w_wipeme, - std::vector& w_idlers, - std::vector& w_pending, - std::vector& w_unstable, - std::vector& w_sendable); + std::vector& w_idleLinks, + std::vector& w_pendingSockets, + std::vector& w_unstableLinks, + std::vector& w_activeLinks); /// Check if a running link is stable. /// @retval true running link is stable @@ -264,7 +264,7 @@ class CUDTGroup /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) /// @param[out] w_parallel Parallel link container (will be filled inside this function) /// @param[out] w_final_stat Status to be reported by this function eventually - /// @param[out] w_max_sendable_weight Maximum weight value of sendable links + /// @param[out] w_maxActiveWeight Maximum weight value of active links /// @param[out] w_nsuccessful Updates the number of successful links /// @param[out] w_is_unstable Set true if sending resulted in AGAIN error. /// @@ -279,7 +279,7 @@ class CUDTGroup int32_t& w_curseq, std::vector& w_parallel, int& w_final_stat, - uint16_t& w_max_sendable_weight, + uint16_t& w_maxActiveWeight, size_t& w_nsuccessful, bool& w_is_unstable); void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc); @@ -297,13 +297,13 @@ class CUDTGroup /// has a higher weight than any link currently active /// (those are collected in 'sendable_pri'). /// If there are no sendable, a new link needs to be activated anyway. - bool sendBackup_IsActivationNeeded(const std::vector& idlers, + bool sendBackup_IsActivationNeeded(const std::vector& idleLinks, const std::vector& unstable, const std::vector& sendable, const uint16_t max_sendable_weight, std::string& activate_reason) const; - size_t sendBackup_TryActivateIdleLink(const std::vector& idlers, + size_t sendBackup_TryActivateIdleLink(const std::vector& idleLinks, const char* buf, const int len, bool& w_none_succeeded, @@ -311,10 +311,13 @@ class CUDTGroup int32_t& w_curseq, int32_t& w_final_stat, CUDTException& w_cx, - std::vector& w_sendstates, std::vector& w_parallel, std::vector& w_wipeme, const std::string& activate_reason); + + /// Check if pending sockets are to be closed. + /// @param[in] pending pending sockets + /// @param[in,out] w_wipeme a list of sockets to be removed from the group void send_CheckPendingSockets(const std::vector& pending, std::vector& w_wipeme); void send_CloseBrokenSockets(std::vector& w_wipeme); void sendBackup_CheckParallelLinks(const std::vector& unstable, From 5066569043f86d8df9ecb627cc20c103e9c81bb7 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 21 Jan 2021 10:55:48 +0100 Subject: [PATCH 032/790] [apps] Added non-blocking option for bonding examples (#1746) --- examples/test-c-client-bonding.c | 117 +++++++++++++++++++++++++--- examples/test-c-server-bonding.c | 127 ++++++++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 15 deletions(-) diff --git a/examples/test-c-client-bonding.c b/examples/test-c-client-bonding.c index f35c01cee..e9cfb72f0 100644 --- a/examples/test-c-client-bonding.c +++ b/examples/test-c-client-bonding.c @@ -40,6 +40,43 @@ struct #define SIZE(array) (sizeof array/sizeof(array[0])) +// Note that in this example application there's a socket +// used first to connect to the service and then it will be +// used for writing. Therefore the same function will be used +// for waiting for the socket to be connected and then to wait +// for write-ready on the socket used for transmission. For a +// model of waiting for read-ready see test-c-server-bonding.c file. +int WaitForWriteReady(int eid, SRTSOCKET ss) +{ + int ready_err[2]; + int ready_err_len = 2; + int ready_out[2]; + int ready_out_len = 2; + + int st = srt_epoll_wait(eid, ready_err, &ready_err_len, ready_out, &ready_out_len, -1, + 0, 0, 0, 0); + + // Note: with indefinite wait time we can either have a connection reported + // or possibly error. Also srt_epoll_wait never returns 0 - at least the number + // of ready connections is reported or -1 is returned for error, including timeout. + if (st < 1) + { + fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + return 0; + } + + // Check if this was reported as error-ready, in which case it doesn't + // matter if read-ready. + if (ready_err[0] == ss) + { + int reason = srt_getrejectreason(ss); + fprintf(stderr, "srt_epoll_wait: socket @%d reported error reason=%d: %s\n", ss, reason, srt_rejectreason_str(reason)); + return 0; + } + + return 1; +} + int main(int argc, char** argv) { int ss, st; @@ -62,18 +99,34 @@ int main(int argc, char** argv) break; } - printf("srt startup\n"); - srt_startup(); - + int is_nonblocking = 0; size_t nmemb = argc - 2; - if (nmemb % 2) + if (nmemb < 2) { - fprintf(stderr, "Usage error: after , pairs are expected.\n"); + fprintf(stderr, "Usage error: no members specified\n"); return 1; } + if (nmemb % 2) + { + // Last argument is then optionset + --nmemb; + const char* opt = argv[argc-1]; + if (strchr(opt, 'n')) + is_nonblocking = 1; + } + nmemb /= 2; + printf("srt startup\n"); + srt_startup(); + + // Declare all variables before any destructive goto. + // In C++ such a code that jumps over initialization would be illegal, + // in C it causes an uninitialized value to be used. + int eid = -1; + int write_modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR; + SRT_SOCKGROUPDATA* grpdata = NULL; SRT_SOCKGROUPCONFIG* grpconfig = calloc(nmemb, sizeof (SRT_SOCKGROUPCONFIG)); printf("srt group\n"); @@ -81,7 +134,7 @@ int main(int argc, char** argv) if (ss == SRT_ERROR) { fprintf(stderr, "srt_create_group: %s\n", srt_getlasterror_str()); - return 1; + goto end; } const int B = 2; @@ -94,12 +147,21 @@ int main(int argc, char** argv) sa.sin_port = htons(atoi(argv[B + 2*i + 1])); if (inet_pton(AF_INET, argv[B + 2*i], &sa.sin_addr) != 1) { - return 1; + fprintf(stderr, "inet_pton: can't resolve address: %s\n", argv[B + 2*i]); + goto end; } grpconfig[i] = srt_prepare_endpoint(NULL, (struct sockaddr*)&sa, sizeof sa); } + if (is_nonblocking) + { + int blockingmode = 0; + srt_setsockflag(ss, SRTO_RCVSYN, &blockingmode, sizeof (blockingmode)); + eid = srt_epoll_create(); + srt_epoll_add_usock(eid, ss, &write_modes); + } + printf("srt connect (group)\n"); // Note: this function unblocks at the moment when at least one connection @@ -109,7 +171,28 @@ int main(int argc, char** argv) if (st == SRT_ERROR) { fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str()); - return 1; + goto end; + } + + // In non-blocking mode the srt_connect function returns immediately + // and displays only errors of the initial usage, not runtime errors. + // These could be reported by epoll. + if (is_nonblocking) + { + // WRITE-ready means connected + + printf("srt wait for socket reporting connection success\n"); + if (!WaitForWriteReady(eid, ss)) + goto end; + } + + // In non-blocking mode now is the time to possibly change the epoll. + // As this socket will be used for writing, it is in the right mode already. + // Just set the right flag, as for non-blocking connect it needs RCVSYN. + if (is_nonblocking) + { + int blockingmode = 0; + srt_setsockflag(ss, SRTO_SNDSYN, &blockingmode, sizeof (blockingmode)); } // Important: Normally you need that at least one link is ready for @@ -123,7 +206,7 @@ int main(int argc, char** argv) printf("sleeping 1s to make it probable all links are established\n"); sleep(1); - SRT_SOCKGROUPDATA* grpdata = calloc(nmemb, sizeof (SRT_SOCKGROUPDATA)); + grpdata = calloc(nmemb, sizeof (SRT_SOCKGROUPDATA)); for (i = 0; i < 100; i++) { @@ -133,11 +216,18 @@ int main(int argc, char** argv) mc.grpdata = grpdata; mc.grpdata_size = nmemb; // Set maximum known + if (is_nonblocking) + { + // Block in epoll as srt_recvmsg2 will not block. + if (!WaitForWriteReady(eid, ss)) + goto end; + } + st = srt_sendmsg2(ss, message, sizeof message, &mc); if (st == SRT_ERROR) { fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str()); - return 1; + goto end; } // Perform the group check. This can be used to recognize broken connections @@ -162,7 +252,11 @@ int main(int argc, char** argv) usleep(1000); // 1 ms } - +end: + if (eid != -1) + { + srt_epoll_release(eid); + } printf("srt close\n"); st = srt_close(ss); if (st == SRT_ERROR) @@ -174,6 +268,7 @@ int main(int argc, char** argv) free(grpdata); free(grpconfig); +//cleanup: printf("srt cleanup\n"); srt_cleanup(); return 0; diff --git a/examples/test-c-server-bonding.c b/examples/test-c-server-bonding.c index 60748df12..76d05f50e 100644 --- a/examples/test-c-server-bonding.c +++ b/examples/test-c-server-bonding.c @@ -22,6 +22,49 @@ #include "srt.h" +// Note that in this example application there's a listening +// socket, off which then a transmission socket is accepted, +// then this socket will be used for reading. Therefore the same +// function will be used for waiting for the listener to get the +// accepted socket ready and then to wait for read-readiness on +// the transmission socket. For a model of waiting for write-ready +// see test-c-client-bonding.c file. +int WaitForReadReady(int eid, SRTSOCKET ss) +{ + int ready_in[2]; + int ready_in_len = 2; + int ready_err[2]; + int ready_err_len = 2; + + int st = srt_epoll_wait(eid, ready_in, &ready_in_len, ready_err, &ready_err_len, -1, + 0, 0, 0, 0); + + // Note: with indefinite wait time we can either have a connection reported + // or possibly error. Also srt_epoll_wait never returns 0 - at least the number + // of ready connections is reported or -1 is returned for error, including timeout. + if (st < 1) + { + fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + return 0; + } + + // Check if this was reported as error-ready, in which case it doesn't + // matter if read-ready. + if (ready_err[0] == ss) + { + fprintf(stderr, "srt_epoll_wait: socket @%d reported error\n", ss); + return 0; + } + + if (ready_in[0] != ss) + { + fprintf(stderr, "srt_epoll_wait: socket @%d not reported ready\n", ss); + return 0; + } + + return 1; +} + int main(int argc, char** argv) { int globstatus = 0; @@ -32,9 +75,10 @@ int main(int argc, char** argv) struct sockaddr_storage their_addr; SRT_SOCKGROUPDATA* grpdata = NULL; - if (argc != 3) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; + if (argc < 3 || argc > 4) + { + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + return 1; } printf("srt startup\n"); @@ -51,6 +95,19 @@ int main(int argc, char** argv) } // Now that the socket is created, jump to 'end' on error. + // Check options + int is_nonblocking = 0; + SRTSOCKET their_fd = SRT_INVALID_SOCK; // declared early because of gotos + int eid = -1; + int lsn_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; + int read_modes = lsn_modes; + if (argc > 3) + { + const char* opt = argv[3]; + if (strchr(opt, 'n')) + is_nonblocking = 1; + } + printf("srt bind address\n"); if (0 == strcmp(argv[1], "0")) { @@ -77,6 +134,14 @@ int main(int argc, char** argv) goto end; } + if (is_nonblocking) + { + int blockingmode = 0; + srt_setsockflag(ss, SRTO_RCVSYN, &blockingmode, sizeof (blockingmode)); + eid = srt_epoll_create(); + srt_epoll_add_usock(eid, ss, &lsn_modes); + } + printf("srt listen\n"); // We set here 10, just for a case. Every unit in this number @@ -91,6 +156,7 @@ int main(int argc, char** argv) goto end; } + // In this example, there will be prepared an array of 10 items. // The listener, however, doesn't know how many member connections // one bonded connection will contain, so a real application should be @@ -98,15 +164,50 @@ int main(int argc, char** argv) const size_t N = 10; grpdata = calloc(N, sizeof (SRT_SOCKGROUPDATA)); + // In non-blocking mode you can't call srt_accept immediately. + // You must first wait for readiness on the listener socket. + if (is_nonblocking) + { + printf("srt wait for listener socket reporting in a new connection\n"); + if (!WaitForReadReady(eid, ss)) + goto end; + } + printf("srt accept\n"); int addr_size = sizeof their_addr; - SRTSOCKET their_fd = srt_accept(ss, (struct sockaddr *)&their_addr, &addr_size); + their_fd = srt_accept(ss, (struct sockaddr *)&their_addr, &addr_size); + + if (their_fd == -1) + { + fprintf(stderr, "srt_accept: %s\n", srt_getlasterror_str()); + goto end; + } + + printf("accepted socket: @%d\n", their_fd); // You never know if `srt_accept` is going to give you a socket or a group. // You have to check it on your own. The SRTO_GROUPCONNECT flag doesn't disallow // single socket connections. int isgroup = their_fd & SRTGROUP_MASK; + if (!isgroup) + { + fprintf(stderr, "srt_accept: Accepted @%d is not a group???\n", their_fd); + goto end; + } + + if (is_nonblocking) + { + // NOTE: The SRTO_RCVSYN = false flag will be derived from + // the listener socket and we are going to read, so it matches + // the need. In case when you'd like to write to the accepted + // socket, you'd have to set also SRTO_SNDSYN = false. + srt_epoll_add_usock(eid, their_fd, &read_modes); + + // The listener socket is no longer important. + srt_epoll_remove_usock(eid, ss); + } + // Still, use the same procedure for receiving, no matter if // this is a bonded or single connection. int i; @@ -117,6 +218,14 @@ int main(int argc, char** argv) SRT_MSGCTRL mc = srt_msgctrl_default; mc.grpdata = grpdata; mc.grpdata_size = N; + + if (is_nonblocking) + { + // Block in epoll as srt_recvmsg2 will not block. + if (!WaitForReadReady(eid, their_fd)) + goto end; + } + st = srt_recvmsg2(their_fd, msg, sizeof msg, &mc); if (st == SRT_ERROR) { @@ -146,8 +255,18 @@ int main(int argc, char** argv) } end: + if (eid != -1) + { + srt_epoll_release(eid); + } free(grpdata); printf("srt close\n"); + st = srt_close(their_fd); // just for a case; broken socket should be wiped out anyway + if (st == SRT_ERROR) + { + fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str()); + // But not matter, we're finishing here. + } st = srt_close(ss); if (st == SRT_ERROR) { From 68edf20ca9e20728d7b08d5fb25727657d7f1e24 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 21 Jan 2021 12:09:45 +0100 Subject: [PATCH 033/790] [apps] Created an example of a non-blocking client (#1753) --- CMakeLists.txt | 3 + examples/example-client-nonblock.c | 165 +++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 examples/example-client-nonblock.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 30dc33c3f..c68167525 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1163,6 +1163,9 @@ if (ENABLE_EXAMPLES) srt_add_example(test-c-client.c) srt_make_application(test-c-client) + srt_add_example(example-client-nonblock.c) + srt_make_application(example-client-nonblock) + srt_add_example(test-c-server.c) srt_make_application(test-c-server) diff --git a/examples/example-client-nonblock.c b/examples/example-client-nonblock.c new file mode 100644 index 000000000..b711f5a38 --- /dev/null +++ b/examples/example-client-nonblock.c @@ -0,0 +1,165 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + */ + + +#include +#include +#ifdef _WIN32 +#define usleep(x) Sleep(x / 1000) +#else +#include +#endif + +#include "srt.h" + +int main(int argc, char** argv) +{ + int ss, st; + struct sockaddr_in sa; + int yes = 1; + const char message [] = "This message should be sent to the other side"; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + printf("SRT startup\n"); + srt_startup(); + + printf("Creating SRT socket\n"); + ss = srt_create_socket(); + if (ss == SRT_ERROR) + { + fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str()); + return 1; + } + + printf("Creating remote address\n"); + sa.sin_family = AF_INET; + sa.sin_port = htons(atoi(argv[2])); + if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1) + { + return 1; + } + + int epollid = srt_epoll_create(); + if (epollid == -1) + { + fprintf(stderr, "srt_epoll_create: %s\n", srt_getlasterror_str()); + return 1; + } + + printf("srt setsockflag\n"); + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) + || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &yes, sizeof yes)) + { + fprintf(stderr, "SRTO_SNDSYN or SRTO_RCVSYN: %s\n", srt_getlasterror_str()); + return 1; + } + + // When a caller is connected, a write-readiness event is triggered. + int modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR; + if (SRT_ERROR == srt_epoll_add_usock(epollid, ss, &modes)) + { + fprintf(stderr, "srt_epoll_add_usock: %s\n", srt_getlasterror_str()); + return 1; + } + + printf("srt connect\n"); + st = srt_connect(ss, (struct sockaddr*)&sa, sizeof sa); + if (st == SRT_ERROR) + { + fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str()); + return 1; + } + + // We had subscribed for write-readiness or error. + // Write readiness comes in wready array, + // error is notified via rready in this case. + int rlen = 1; + SRTSOCKET rready; + int wlen = 1; + SRTSOCKET wready; + if (srt_epoll_wait(epollid, &rready, &rlen, &wready, &wlen, -1, 0, 0, 0, 0) != -1) + { + SRT_SOCKSTATUS state = srt_getsockstate(ss); + if (state != SRTS_CONNECTED || rlen > 0) // rlen > 0 - an error notification + { + fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + return 1; + } + + if (wlen != 1 || wready != ss) + { + fprintf(stderr, "srt_epoll_wait: wlen %d, wready %d, socket %d\n", wlen, wready, ss); + return 1; + } + } + else + { + fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str()); + return 1; + } + + int i; + for (i = 0; i < 100; i++) + { + rready = SRT_INVALID_SOCK; + rlen = 1; + wready = SRT_INVALID_SOCK; + wlen = 1; + + // As we have subscribed only for write-readiness or error events, + // but have not subscribed for read-readiness, + // through readfds we are notified about an error. + int timeout_ms = 5000; // ms + int res = srt_epoll_wait(epollid, &rready, &rlen, &wready, &wlen, timeout_ms, 0, 0, 0, 0); + if (res == SRT_ERROR || rlen > 0) + { + fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + return 1; + } + + printf("srt sendmsg2 #%d >> %s\n", i, message); + st = srt_sendmsg2(ss, message, sizeof message, NULL); + if (st == SRT_ERROR) + { + fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str()); + return 1; + } + + usleep(1000); // 1 ms + } + + // Let's wait a bit so that all packets reach destination + usleep(100000); // 100 ms + + // In live mode the connection will be closed even if some packets were not yet acknowledged. + printf("srt close\n"); + st = srt_close(ss); + if (st == SRT_ERROR) + { + fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str()); + return 1; + } + + printf("srt cleanup\n"); + srt_cleanup(); + return 0; +} From 40aafa28c881bf65c164f26b1291bf50cc0f1dd4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 21 Jan 2021 15:01:42 +0100 Subject: [PATCH 034/790] [core] Added socket ID to RCV-DROP log message --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 59076d3d2..04abd3217 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5676,7 +5676,7 @@ void *CUDT::tsbpd(void *param) << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif LOGC(brlog.Warn, - log << "RCV-DROPPED " << seqlen << " packet(s), packet seqno %" << skiptoseqno + log << self->CONID() << "RCV-DROPPED " << seqlen << " packet(s), packet seqno %" << skiptoseqno << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif From b2d35fc74e34a56df01fab4607231049afa3910f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 22 Jan 2021 18:18:09 +0100 Subject: [PATCH 035/790] [core] Splitting sendBackup_CheckParallelLinks(..) (#1751) sendBackup_RetryWaitBlocked(..) retries sending. sendBackup_SilenceRedundantLinks(..) silences redundant parallel links. --- srtcore/group.cpp | 453 +++++++++++++++++++++++----------------------- srtcore/group.h | 14 +- 2 files changed, 239 insertions(+), 228 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c664094aa..426ae67eb 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3540,12 +3540,12 @@ struct FByOldestActive }; // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstableLinks, - vector& w_parallel, - int& w_final_stat, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - CUDTException& w_cx) +void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, + vector& w_parallel, + int& w_final_stat, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + CUDTException& w_cx) { // In contradiction to broadcast sending, backup sending must check // the blocking state in total first. We need this information through @@ -3560,277 +3560,285 @@ void CUDTGroup::sendBackup_CheckParallelLinks(const vector& unstableLinks // unstable links and none other or others just got broken), continue sending // anyway. -#if ENABLE_HEAVY_LOGGING - // Potential problem to be checked in developer mode - for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) - { - if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) - { - LOGC(gslog.Debug, - log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); - } - } -#endif // This procedure is for a case when the packet could not be sent // over any link (hence "none succeeded"), but there are some unstable // links and no parallel links. We need to WAIT for any of the links // to become available for sending. - if (w_parallel.empty() && !unstableLinks.empty() && w_none_succeeded) + if (!w_parallel.empty() || unstableLinks.empty() || !w_none_succeeded) + return; + + + HLOGC(gslog.Debug, log << "grp/sendBackup: no parallel links and " + << unstableLinks.size() << " unstable links - checking..."); + + // Note: GroupLock is set already, skip locks and checks + getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); + m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + + if (m_SndEpolld->watch_empty()) { - HLOGC(gslog.Debug, log << "grp/sendBackup: no parallel links and " - << unstableLinks.size() << " unstable links - checking..."); + // wipeme wiped, pending sockets checked, it can only mean that + // all sockets are broken. + HLOGC(gslog.Debug, log << "grp/sendBackup: epolld empty - all sockets broken?"); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } - // Note: GroupLock is set already, skip locks and checks - getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + if (!m_bSynSending) + { + HLOGC(gslog.Debug, log << "grp/sendBackup: non-blocking mode - exit with no-write-ready"); + throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); + } + // Here is the situation that the only links left here are: + // - those that failed to send (already closed and wiped out) + // - those that got blockade on sending + // At least, there was so far no socket through which we could + // successfully send anything. + + // As a last resort in this situation, try to wait for any links + // remaining in the group to become ready to write. + + CEPoll::fmap_t sready; + int brdy; + + // This keeps the number of links that existed at the entry. + // Simply notify all dead links, regardless as to whether the number + // of group members decreases below. If the number of corpses reaches + // this number, consider the group connection broken. + size_t nlinks = m_Group.size(); + size_t ndead = 0; + +RetryWaitBlocked: + { + // Some sockets could have been closed in the meantime. if (m_SndEpolld->watch_empty()) { - // wipeme wiped, pending sockets checked, it can only mean that - // all sockets are broken. - HLOGC(gslog.Debug, log << "grp/sendBackup: epolld empty - all sockets broken?"); + HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } - if (!m_bSynSending) + InvertedLock ug(m_GroupLock); + HLOGC(gslog.Debug, + log << "grp/sendBackup: swait call to get at least one link alive up to " << m_iSndTimeOut << "us"); + THREAD_PAUSED(); + brdy = m_pGlobal->m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); + THREAD_RESUMED(); + + if (brdy == 0) // SND timeout exceeded { - HLOGC(gslog.Debug, log << "grp/sendBackup: non-blocking mode - exit with no-write-ready"); throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); } - // Here is the situation that the only links left here are: - // - those that failed to send (already closed and wiped out) - // - those that got blockade on sending - - // At least, there was so far no socket through which we could - // successfully send anything. - - // As a last resort in this situation, try to wait for any links - // remaining in the group to become ready to write. - CEPoll::fmap_t sready; - int brdy; - - // This keeps the number of links that existed at the entry. - // Simply notify all dead links, regardless as to whether the number - // of group members decreases below. If the number of corpses reaches - // this number, consider the group connection broken. - size_t nlinks = m_Group.size(); - size_t ndead = 0; + HLOGC(gslog.Debug, log << "grp/sendBackup: swait exited with " << brdy << " ready sockets:"); -RetryWaitBlocked: + // Check if there's anything in the "error" section. + // This must be cleared here before the lock on group is set again. + // (This loop will not fire neither once if no failed sockets found). + for (CEPoll::fmap_t::const_iterator i = sready.begin(); i != sready.end(); ++i) { - // Some sockets could have been closed in the meantime. - if (m_SndEpolld->watch_empty()) + if (i->second & SRT_EPOLL_ERR) { - HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } - - InvertedLock ug(m_GroupLock); - HLOGC(gslog.Debug, - log << "grp/sendBackup: swait call to get at least one link alive up to " << m_iSndTimeOut << "us"); - THREAD_PAUSED(); - brdy = m_pGlobal->m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); - THREAD_RESUMED(); + SRTSOCKET id = i->first; + CUDTSocket* s = m_pGlobal->locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! + if (s) + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: swait/ex on @" << (id) + << " while waiting for any writable socket - CLOSING"); + CUDT::s_UDTUnited.close(s); // << LOCKS m_GlobControlLock, then GroupLock! + } + else + { + HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " - WAS DELETED IN THE MEANTIME"); + } - if (brdy == 0) // SND timeout exceeded - { - throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); + ++ndead; } + } + HLOGC(gslog.Debug, log << "grp/sendBackup: swait/?close done, re-acquiring GroupLock"); + } - HLOGC(gslog.Debug, log << "grp/sendBackup: swait exited with " << brdy << " ready sockets:"); + // GroupLock is locked back - // Check if there's anything in the "error" section. - // This must be cleared here before the lock on group is set again. - // (This loop will not fire neither once if no failed sockets found). - for (CEPoll::fmap_t::const_iterator i = sready.begin(); i != sready.end(); ++i) - { - if (i->second & SRT_EPOLL_ERR) - { - SRTSOCKET id = i->first; - CUDTSocket* s = m_pGlobal->locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! - if (s) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: swait/ex on @" << (id) - << " while waiting for any writable socket - CLOSING"); - CUDT::s_UDTUnited.close(s); // << LOCKS m_GlobControlLock, then GroupLock! - } - else - { - HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " - WAS DELETED IN THE MEANTIME"); - } + // Re-check after the waiting lock has been reacquired + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - ++ndead; - } - } - HLOGC(gslog.Debug, log << "grp/sendBackup: swait/?close done, re-acquiring GroupLock"); - } + if (brdy == -1 || ndead >= nlinks) + { + LOGC(gslog.Error, + log << "grp/sendBackup: swait=>" << brdy << " nlinks=" << nlinks << " ndead=" << ndead + << " - looxlike all links broken"); + m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + // You can safely throw here - nothing to fill in when all sockets down. + // (timeout was reported by exception in the swait call). + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } - // GroupLock is locked back + // Ok, now check if we have at least one write-ready. + // Note that the procedure of activation of a new link in case of + // no stable links found embraces also rexmit-sending and status + // check as well, including blocked status. - // Re-check after the waiting lock has been reacquired - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + // Find which one it was. This is so rare case that we can + // suffer linear search. - if (brdy == -1 || ndead >= nlinks) + int nwaiting = 0; + int nactivated ATR_UNUSED = 0; + int stat = -1; + for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) + { + // int erc = 0; + + // Skip if not writable in this run + if (!CEPoll::isready(sready, d->id, SRT_EPOLL_OUT)) { - LOGC(gslog.Error, - log << "grp/sendBackup: swait=>" << brdy << " nlinks=" << nlinks << " ndead=" << ndead - << " - looxlike all links broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); - // You can safely throw here - nothing to fill in when all sockets down. - // (timeout was reported by exception in the swait call). - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + ++nwaiting; + HLOGC(gslog.Debug, log << "grp/sendBackup: @" << d->id << " NOT ready:OUT, added as waiting"); + continue; } - // Ok, now check if we have at least one write-ready. - // Note that the procedure of activation of a new link in case of - // no stable links found embraces also rexmit-sending and status - // check as well, including blocked status. - - // Find which one it was. This is so rare case that we can - // suffer linear search. - - int nwaiting = 0; - int nactivated ATR_UNUSED = 0; - int stat = -1; - for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) + if (d->sndstate == SRT_GST_RUNNING) { - // int erc = 0; + HLOGC(gslog.Debug, + log << "grp/sendBackup: link @" << d->id << " RUNNING - SKIPPING from activate and resend"); + continue; + } - // Skip if not writable in this run - if (!CEPoll::isready(sready, d->id, SRT_EPOLL_OUT)) - { - ++nwaiting; - HLOGC(gslog.Debug, log << "grp/sendBackup: @" << d->id << " NOT ready:OUT, added as waiting"); - continue; - } + try + { + // Note: this will set the currently required packet + // because it has been just freshly added to the sender buffer + stat = sendBackupRexmit(d->ps->core(), (w_mc)); + ++nactivated; + } + catch (CUDTException& e) + { + // This will be propagated from internal sendmsg2 call, + // but that's ok - we want this sending interrupted even in half. + w_cx = e; + stat = -1; + // erc = e.getErrorCode(); + } - if (d->sndstate == SRT_GST_RUNNING) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link @" << d->id << " RUNNING - SKIPPING from activate and resend"); - continue; - } + d->sndresult = stat; + d->laststatus = d->ps->getStatus(); - try - { - // Note: this will set the currently required packet - // because it has been just freshly added to the sender buffer - stat = sendBackupRexmit(d->ps->core(), (w_mc)); - ++nactivated; - } - catch (CUDTException& e) - { - // This will be propagated from internal sendmsg2 call, - // but that's ok - we want this sending interrupted even in half. - w_cx = e; - stat = -1; - // erc = e.getErrorCode(); - } + if (stat == -1) + { + // This link is no longer waiting. + continue; + } - d->sndresult = stat; - d->laststatus = d->ps->getStatus(); + w_parallel.push_back(d); + w_final_stat = stat; + steady_clock::time_point currtime = steady_clock::now(); + d->ps->core().m_tsTmpActiveSince = currtime; + d->sndstate = SRT_GST_RUNNING; + w_none_succeeded = false; + HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); - if (stat == -1) - { - // This link is no longer waiting. - continue; - } + break; + } - w_parallel.push_back(d); - w_final_stat = stat; - steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsTmpActiveSince = currtime; - d->sndstate = SRT_GST_RUNNING; - w_none_succeeded = false; - HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); + // If we have no links successfully activated, but at least + // one link "not ready for writing", continue waiting for at + // least one link ready. + if (stat == -1 && nwaiting > 0) + { + HLOGC(gslog.Debug, log << "grp/sendBackup: still have " << nwaiting << " waiting and none succeeded, REPEAT"); + goto RetryWaitBlocked; + } - break; - } + HLOGC(gslog.Debug, log << "grp/sendBackup: " << nactivated << " links activated with " + << unstableLinks.size() << " unstable"); +} - // If we have no links successfully activated, but at least - // one link "not ready for writing", continue waiting for at - // least one link ready. - if (stat == -1 && nwaiting > 0) +// [[using locked(this->m_GroupLock)]] +void CUDTGroup::sendBackup_SilenceRedundantLinks(const vector& unstableLinks, + vector& w_parallel) +{ +#if ENABLE_HEAVY_LOGGING + // Potential problem to be checked in developer mode + for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) + { + if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) { - HLOGC(gslog.Debug, log << "grp/sendBackup: still have " << nwaiting << " waiting and none succeeded, REPEAT"); - goto RetryWaitBlocked; + LOGC(gslog.Debug, + log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); } - - HLOGC(gslog.Debug, log << "grp/sendBackup: " << nactivated << " links activated with " - << unstableLinks.size() << " unstable"); } +#endif + // The most important principle is to keep the data being sent constantly, // even if it means temporarily full redundancy. However, if you are certain // that you have multiple stable links running at the moment, SILENCE all but // the one with highest priority. - if (w_parallel.size() > 1) - { - sort(w_parallel.begin(), w_parallel.end(), FPriorityOrder()); - steady_clock::time_point currtime = steady_clock::now(); + if (w_parallel.size() <= 1) + return; - vector::iterator b = w_parallel.begin(); + sort(w_parallel.begin(), w_parallel.end(), FPriorityOrder()); + steady_clock::time_point currtime = steady_clock::now(); - // Additional criterion: if you have multiple links with the same weight, - // check if you have at least one with m_tsTmpActiveSince == 0. If not, - // sort them additionally by this time. + vector::iterator b = w_parallel.begin(); - vector::iterator b1 = b, e = ++b1; + // Additional criterion: if you have multiple links with the same weight, + // check if you have at least one with m_tsTmpActiveSince == 0. If not, + // sort them additionally by this time. - // Both e and b1 stand on b+1 position. - // We have a guarantee that b+1 still points to a valid element. - while (e != w_parallel.end()) - { - if ((*e)->weight != (*b)->weight) - break; - ++e; - } + vector::iterator b1 = b, e = ++b1; + + // Both e and b1 stand on b+1 position. + // We have a guarantee that b+1 still points to a valid element. + while (e != w_parallel.end()) + { + if ((*e)->weight != (*b)->weight) + break; + ++e; + } + + if (b1 != e) + { + // More than 1 link with the same weight. Sorting them according + // to a different criterion will not change the previous sorting order + // because the elements in this range are equal according to the previous + // criterion. + // Here find the link with least time. The "trap" zero time matches this + // requirement, occasionally. + sort(b, e, FByOldestActive()); + } - if (b1 != e) + // After finding the link to leave active, leave it behind. + HLOGC(gslog.Debug, log << "grp/sendBackup: keeping parallel link @" << (*b)->id << " and silencing others:"); + ++b; + for (; b != w_parallel.end(); ++b) + { + gli_t& d = *b; + if (d->sndstate != SRT_GST_RUNNING) { - // More than 1 link with the same weight. Sorting them according - // to a different criterion will not change the previous sorting order - // because the elements in this range are equal according to the previous - // criterion. - // Here find the link with least time. The "trap" zero time matches this - // requirement, occasionally. - sort(b, e, FByOldestActive()); + LOGC(gslog.Error, + log << "grp/sendBackup: IPE: parallel link container contains non-running link @" << d->id); + continue; } - - // After finding the link to leave active, leave it behind. - HLOGC(gslog.Debug, log << "grp/sendBackup: keeping parallel link @" << (*b)->id << " and silencing others:"); - ++b; - for (; b != w_parallel.end(); ++b) + CUDT& ce = d->ps->core(); + steady_clock::duration td(0); + if (!is_zero(ce.m_tsTmpActiveSince) && + count_microseconds(td = currtime - ce.m_tsTmpActiveSince) < ce.m_uOPT_StabilityTimeout) { - gli_t& d = *b; - if (d->sndstate != SRT_GST_RUNNING) - { - LOGC(gslog.Error, - log << "grp/sendBackup: IPE: parallel link container contains non-running link @" << d->id); - continue; - } - CUDT& ce = d->ps->core(); - steady_clock::duration td(0); - if (!is_zero(ce.m_tsTmpActiveSince) && - count_microseconds(td = currtime - ce.m_tsTmpActiveSince) < ce.m_uOPT_StabilityTimeout) - { - HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " - << ce.m_uOPT_StabilityTimeout << "(stability timeout)"); - continue; - } - - // Clear activation time because the link is no longer active! - d->sndstate = SRT_GST_IDLE; - HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsTmpActiveSince)); - ce.m_tsTmpActiveSince = steady_clock::time_point(); + HLOGC(gslog.Debug, + log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " + << ce.m_uOPT_StabilityTimeout << "(stability timeout)"); + continue; } + + // Clear activation time because the link is no longer active! + d->sndstate = SRT_GST_IDLE; + HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsTmpActiveSince)); + ce.m_tsTmpActiveSince = steady_clock::time_point(); } } @@ -3966,7 +3974,6 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (is_unstable && is_zero(u.m_tsUnstableSince)) // Add to unstable only if it wasn't unstable already insert_uniq((unstableLinks), d); - const Sendstate cstate = {d->id, &*d, stat, erc}; d->sndresult = stat; d->laststatus = d->ps->getStatus(); } @@ -4091,7 +4098,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - sendBackup_CheckParallelLinks(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); + sendBackup_RetryWaitBlocked(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); + + sendBackup_SilenceRedundantLinks(unstableLinks, (parallel)); // (closing condition checked inside this call) if (none_succeeded) diff --git a/srtcore/group.h b/srtcore/group.h index 2196dd4ce..e1853babe 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -320,12 +320,14 @@ class CUDTGroup /// @param[in,out] w_wipeme a list of sockets to be removed from the group void send_CheckPendingSockets(const std::vector& pending, std::vector& w_wipeme); void send_CloseBrokenSockets(std::vector& w_wipeme); - void sendBackup_CheckParallelLinks(const std::vector& unstable, - std::vector& w_parallel, - int& w_final_stat, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - CUDTException& w_cx); + void sendBackup_RetryWaitBlocked(const std::vector& unstable, + std::vector& w_parallel, + int& w_final_stat, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + CUDTException& w_cx); + void sendBackup_SilenceRedundantLinks(const std::vector& unstable, + std::vector& w_parallel); void send_CheckValidSockets(); From 0f8623e6f4f6c7a4526b746a0d7a535ef02228ef Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 25 Jan 2021 15:48:41 +0100 Subject: [PATCH 036/790] [core] Fixed too early closed caller socket in background. (#1750) - Updated documentation - Lifted rules for a test that should result in the late break on the listener side --- docs/API-functions.md | 5 +++++ docs/API.md | 9 +++++++++ srtcore/queue.cpp | 10 +++++++++- test/test_connection_timeout.cpp | 26 +++++++++++++------------- test/test_enforced_encryption.cpp | 19 +++++++++++-------- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index 1f4834c83..bb08be06f 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -783,6 +783,11 @@ is through the epoll flag with [`SRT_EPOLL_ERR`](#SRT_EPOLL_ERR). In this case y also call [`srt_getrejectreason`](#srt_getrejectreason) to get the detailed reason for the error, including connection timeout ([`SRT_REJ_TIMEOUT`](#SRT_REJ_TIMEOUT)). +Note that in case of failure the socket is in `SRTS_CONNECTING`, not in +`SRTS_BROKEN` state. After the failure was reported and you read any extra +information from the socket, the socket should be manually closed using +`srt_close` function. + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) diff --git a/docs/API.md b/docs/API.md index 328faa463..a124dac03 100644 --- a/docs/API.md +++ b/docs/API.md @@ -154,6 +154,15 @@ and `remote_name` must use the same port. The peer to which this is going to con should call the same function, with appropriate local and remote addresses. A rendezvous connection means that both parties connect to one another simultaneously. +**IMPORTANT**: The connection may fail, but the socket that was used for connecting +is not automatically closed and it's also not in broken state (broken state can be +only if a socket was first successfully connected and then broken). When using blocking +mode, the connection failure will result in reporting an error from this function call. +In non-blocking mode the connection failure is designated by the `SRT_EPOLL_ERR` flag +set for this socket in the epoll container. After that failure you can read an extra +information from the socket using `srt_getrejectreason` function, and then you should +close the socket. + ### Listener (Server) Example ```c++ diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 615c8ce2c..8daa31ea4 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1098,7 +1098,15 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); i->u->m_bConnecting = false; - i->u->updateBrokenConnection(); + + // DO NOT close the socket here because in this case it might be + // unable to get status from at the right moment. Also only member + // sockets should be taken care of internally - single sockets should + // be normally closed by the application, after it is done with them. + + // app can call any UDT API to learn the connection_broken error + CUDT::s_UDTUnited.m_EPoll.update_events(i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + i->u->completeBrokenConnectionDependencies(i->errorcode); } diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 5d0548029..bbd87e145 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -172,20 +172,20 @@ TEST_F(TestConnectionTimeout, Nonblocking) { */ TEST_F(TestConnectionTimeout, BlockingLoop) { - const sockaddr* psa = reinterpret_cast(&m_sa); + const SRTSOCKET client_sock = srt_create_socket(); + ASSERT_GT(client_sock, 0); // socket_id should be > 0 + + // Set connection timeout to 999 ms to reduce the test execution time. + // Also need to hit a time point between two threads: + // srt_connect will check TTL every second, + // CRcvQueue::worker will wait on a socket for 10 ms. + // Need to have a condition, when srt_connect will process the timeout. const int connection_timeout_ms = 999; + EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); + + const sockaddr* psa = reinterpret_cast(&m_sa); for (int i = 0; i < 10; ++i) { - const SRTSOCKET client_sock = srt_create_socket(); - ASSERT_GT(client_sock, 0); // socket_id should be > 0 - - // Set connection timeout to 999 ms to reduce the test execution time. - // Also need to hit a time point between two threads: - // srt_connect will check TTL every second, - // CRcvQueue::worker will wait on a socket for 10 ms. - // Need to have a condition, when srt_connect will process the timeout. - EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); - EXPECT_EQ(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR); const int error_code = srt_getlasterror(nullptr); @@ -196,9 +196,9 @@ TEST_F(TestConnectionTimeout, BlockingLoop) << error_code << " " << srt_getlasterror_str() << "\n"; break; } - - EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); } + + EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); } diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index c50afb416..cdb0ac719 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -415,22 +415,25 @@ class TestEnforcedEncryption std::this_thread::sleep_for(std::chrono::milliseconds(50)); } while (!caller_done); - const SRT_SOCKSTATUS status = srt_getsockstate(accepted_socket); - if (m_is_tracing) - { - std::cerr << "LATE Socket state accepted: " << m_socket_state[status] - << " (expected: " << m_socket_state[expect.socket_state[CHECK_SOCKET_ACCEPTED]] << ")\n"; - } + // Special case when the expected state is "broken": if so, tolerate every possible + // socket state, just NOT LESS than SRTS_BROKEN, and also don't read any flags on that socket. if (expect.socket_state[CHECK_SOCKET_ACCEPTED] == SRTS_BROKEN) { - EXPECT_TRUE(accepted_socket == -1 || status == SRTS_BROKEN || status == SRTS_CLOSED); + EXPECT_GE(srt_getsockstate(accepted_socket), SRTS_BROKEN); } else { - EXPECT_EQ(status, expect.socket_state[CHECK_SOCKET_ACCEPTED]); + EXPECT_EQ(srt_getsockstate(accepted_socket), expect.socket_state[CHECK_SOCKET_ACCEPTED]); EXPECT_EQ(GetSocetkOption(accepted_socket, SRTO_SNDKMSTATE), expect.km_state[CHECK_SOCKET_ACCEPTED]); } + + if (m_is_tracing) + { + const SRT_SOCKSTATUS status = srt_getsockstate(accepted_socket); + std::cerr << "LATE Socket state accepted: " << m_socket_state[status] + << " (expected: " << m_socket_state[expect.socket_state[CHECK_SOCKET_ACCEPTED]] << ")\n"; + } } }); From 954968ae3da440cd8fbc13c673ffc493456f8c3d Mon Sep 17 00:00:00 2001 From: Gavin Smith Date: Fri, 29 Jan 2021 01:50:29 +0000 Subject: [PATCH 037/790] [docs] Fixed bad path to 'Contributing' md (which was moved). --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 70bcefbb1..350f89f1d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ As audio/video packets are streamed from a source to a destination device, SRT d * SRT Cookbook: [website](https://srtlab.github.io/srt-cookbook), [GitHub](https://github.com/SRTLab/srt-cookbook) * SRT RFC: [txt](https://haivision.github.io/srt-rfc/draft-sharabayko-mops-srt.txt), [html](https://haivision.github.io/srt-rfc/draft-sharabayko-mops-srt.html), [GitHub](https://github.com/Haivision/srt-rfc) * [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) -* [Contributing](docs/Contributing.md) +* [Contributing](CONTRIBUTING.md) * [Developer's Guide](docs/DevelopersGuide.md) * [SRT Encryption](docs/encryption.md) * [API](docs/API.md) From 0d0888a56943898ab9b537304a94688df565032b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 29 Jan 2021 08:43:42 +0100 Subject: [PATCH 038/790] [build] Fixed CMake CMP0048 policy restriction (#1765) --- CMakeLists.txt | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c68167525..c5f06d3f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,22 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -# XXX This can be potentially done in future, but there still exist -# some dependent project using cmake 2.8 - this can't be done this way. -#cmake_minimum_required (VERSION 3.0.2 FATAL_ERROR) -#project(SRT VERSION "1.4.2") -project(SRT C CXX) +set (SRT_VERSION 1.4.2) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") -include(haiUtil) +include(haiUtil) # needed for set_version_variables +# CMake version 3.0 introduced the VERSION option of the project() command +# to specify a project version as well as the name. +if(${CMAKE_VERSION} VERSION_LESS "3.0.0") + project(SRT C CXX) + # Sets SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH + set_version_variables(SRT_VERSION ${SRT_VERSION}) +else() + cmake_policy(SET CMP0048 NEW) + # Also sets SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH + project(SRT VERSION ${SRT_VERSION} LANGUAGES C CXX) +endif() + include(FindPkgConfig) # XXX See 'if (MINGW)' condition below, may need fixing. include(FindThreads) @@ -43,9 +51,6 @@ if (NOT DEFINED CMAKE_INSTALL_LIBDIR) include(GNUInstallDirs) endif() -set (SRT_VERSION 1.4.2) -set_version_variables(SRT_VERSION ${SRT_VERSION}) - # The CMAKE_BUILD_TYPE seems not to be always set, weird. if (NOT DEFINED ENABLE_DEBUG) @@ -775,8 +780,6 @@ MafReadDir(srtcore filelist.maf PRIVATE_HEADERS HEADERS_srt_private ) -message(STATUS "SRT Sources: ${SOURCES_srt}") - # Auto generated version file and add it to the HEADERS_srt list. if(DEFINED ENV{APPVEYOR_BUILD_NUMBER}) set(SRT_VERSION_BUILD ON) @@ -1199,8 +1202,6 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) MafReadDir(test filelist.maf SOURCES SOURCES_unittests ) - - message(STATUS "Unit test sources: ${SOURCES_unittests}") srt_add_program(test-srt ${SOURCES_unittests}) srt_make_application(test-srt) From 74fc74acb53b313a4b3487414230ef351a790130 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 29 Jan 2021 08:46:13 +0100 Subject: [PATCH 039/790] [core] Increased FormatTime precision (#1766) Changed clock suffixes STD -> STDY, SYS -> SYST --- srtcore/sync.cpp | 22 +++++++++------------- srtcore/sync.h | 9 +++++++-- srtcore/sync_cxx11.cpp | 30 ++++++++++++++++++++++++++++++ srtcore/sync_posix.cpp | 24 +++++++++++++++++------- test/test_sync.cpp | 12 ++++++------ 5 files changed, 69 insertions(+), 28 deletions(-) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 8f0c7c1c8..45d568212 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -34,26 +34,22 @@ std::string FormatTime(const steady_clock::time_point& timestamp) if (is_zero(timestamp)) { // Use special string for 0 - return "00:00:00.000000"; + return "00:00:00.000000 [STDY]"; } - const uint64_t total_us = count_microseconds(timestamp.time_since_epoch()); - const uint64_t us = total_us % 1000000; - const uint64_t total_sec = total_us / 1000000; - - const uint64_t days = total_sec / (60 * 60 * 24); + const int decimals = clockSubsecondPrecision(); + const uint64_t total_sec = count_seconds(timestamp.time_since_epoch()); + const uint64_t days = total_sec / (60 * 60 * 24); const uint64_t hours = total_sec / (60 * 60) - days * 24; - const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; - ostringstream out; if (days) out << days << "D "; - out << setfill('0') << setw(2) << hours << ":" - << setfill('0') << setw(2) << minutes << ":" - << setfill('0') << setw(2) << seconds << "." - << setfill('0') << setw(6) << us << " [STD]"; + out << setfill('0') << setw(2) << hours << ":" + << setfill('0') << setw(2) << minutes << ":" + << setfill('0') << setw(2) << seconds << "." + << setfill('0') << setw(decimals) << timestamp.time_since_epoch().count() << " [STDY]"; return out.str(); } @@ -70,7 +66,7 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) strftime(tmp_buf, 512, "%X.", &tm); ostringstream out; - out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYS]"; + out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYST]"; return out.str(); } diff --git a/srtcore/sync.h b/srtcore/sync.h index de739380c..59c125a75 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -217,6 +217,11 @@ inline Duration operator*(const int& lhs, const Duration +int pow10(); + +template <> +int pow10<10>() +{ + return 1; +} + +template +int pow10() +{ + return 1 + pow10(); +} +} + +int srt::sync::clockSubsecondPrecision() +{ + const int64_t ticks_per_sec = (srt::sync::steady_clock::period::den / srt::sync::steady_clock::period::num); + const int decimals = pow10(); + return decimals; +} + //////////////////////////////////////////////////////////////////////////////// // // SyncCond (based on stl chrono C++11) diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 8c001ad62..d40e23713 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -114,8 +114,18 @@ int64_t get_cpu_frequency() return frequency; } -const int64_t s_cpu_frequency = get_cpu_frequency(); +static int count_subsecond_precision(int64_t ticks_per_us) +{ + int signs = 6; // starting from 1 us + while (ticks_per_us /= 10) ++signs; + return signs; +} + +const int64_t s_clock_ticks_per_us = get_cpu_frequency(); + +const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); +int clockSubsecondPrecision() { return s_clock_subsecond_precision; } } // namespace sync } // namespace srt @@ -155,32 +165,32 @@ srt::sync::TimePoint srt::sync::steady_clock::now() int64_t srt::sync::count_microseconds(const steady_clock::duration& t) { - return t.count() / s_cpu_frequency; + return t.count() / s_clock_ticks_per_us; } int64_t srt::sync::count_milliseconds(const steady_clock::duration& t) { - return t.count() / s_cpu_frequency / 1000; + return t.count() / s_clock_ticks_per_us / 1000; } int64_t srt::sync::count_seconds(const steady_clock::duration& t) { - return t.count() / s_cpu_frequency / 1000000; + return t.count() / s_clock_ticks_per_us / 1000000; } srt::sync::steady_clock::duration srt::sync::microseconds_from(int64_t t_us) { - return steady_clock::duration(t_us * s_cpu_frequency); + return steady_clock::duration(t_us * s_clock_ticks_per_us); } srt::sync::steady_clock::duration srt::sync::milliseconds_from(int64_t t_ms) { - return steady_clock::duration((1000 * t_ms) * s_cpu_frequency); + return steady_clock::duration((1000 * t_ms) * s_clock_ticks_per_us); } srt::sync::steady_clock::duration srt::sync::seconds_from(int64_t t_s) { - return steady_clock::duration((1000000 * t_s) * s_cpu_frequency); + return steady_clock::duration((1000000 * t_s) * s_clock_ticks_per_us); } srt::sync::Mutex::Mutex() diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 2a69c1980..713d190ff 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -586,7 +586,7 @@ TEST(Sync, FormatTime) { auto parse_time = [](const string& timestr) -> long long { // Example string: 1D 02:10:55.972651 [STD] - const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[STD\\]"); + const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6,}) \\[STDY\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); EXPECT_LE(sm.size(), 6); @@ -595,10 +595,10 @@ TEST(Sync, FormatTime) // Day may be missing if zero const long long d = sm[1].matched ? std::stoi(sm[1]) : 0; - const long long h = std::stoi(sm[2]); - const long long m = std::stoi(sm[3]); - const long long s = std::stoi(sm[4]); - const long long u = std::stoi(sm[5]); + const long long h = std::stoll(sm[2]); + const long long m = std::stoll(sm[3]); + const long long s = std::stoll(sm[4]); + const long long u = std::stoll(sm[5]); return u + s * 1000000 + m * 60000000 + h * 60 * 60 * 1000000 + d * 24 * 60 * 60 * 1000000; }; @@ -630,7 +630,7 @@ TEST(Sync, FormatTime) TEST(Sync, FormatTimeSys) { auto parse_time = [](const string& timestr) -> long long { - const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYS\\]"); + const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYST\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); EXPECT_EQ(sm.size(), 5); From 3cc7c3060bb0a1c15685f402ddca89c51437a23e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 29 Jan 2021 08:48:09 +0100 Subject: [PATCH 040/790] [apps] Refactored stats tables with universal printers (#1743) --- apps/apputil.cpp | 259 +++++++++++++++++++++++++------------ apps/apputil.hpp | 62 ++++++++- apps/srt-live-transmit.cpp | 3 +- docs/srt-live-transmit.md | 2 +- testing/srt-test-live.cpp | 15 ++- 5 files changed, 252 insertions(+), 89 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index ecce703e8..9e9ab4f2c 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -351,58 +351,157 @@ string OptionHelpItem(const OptionName& o) // Stats module +template +inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, + TYPE CBytePerfMon::*field) +{ + return new SrtStatDataType(cat, name, longname, field); +} + +#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field)) +#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field) + +vector> g_SrtStatsTable; + +struct SrtStatsTableInit +{ + SrtStatsTableInit(vector>& s) + { + STATX(GEN, time, Time, msTimeStamp); + + STAT(WINDOW, flow, pktFlowWindow); + STAT(WINDOW, congestion, pktCongestionWindow); + STAT(WINDOW, flight, pktFlightSize); + + STAT(LINK, rtt, msRTT); + STAT(LINK, bandwidth, mbpsBandwidth); + STAT(LINK, maxBandwidth, mbpsMaxBW); + + STAT(SEND, packets, pktSent); + STAT(SEND, packetsUnique, pktSentUnique); + STAT(SEND, packetsLost, pktSndLoss); + STAT(SEND, packetsDropped, pktSndDrop); + STAT(SEND, packetsRetransmitted, pktRetrans); + STAT(SEND, packetsFilterExtra, pktSndFilterExtra); + STAT(SEND, bytes, byteSent); + STAT(SEND, bytesUnique, byteSentUnique); + STAT(SEND, bytesDropped, byteSndDrop); + STAT(SEND, mbitRate, mbpsSendRate); + STAT(SEND, sendPeriod, usPktSndPeriod); + //STAT(SEND, msAvgResponseTime, msAvgResponseTime); + //STAT(SEND, msMaxResponseTime, msMaxResponseTime); + + STAT(RECV, packets, pktRecv); + STAT(RECV, packetsUnique, pktRecvUnique); + STAT(RECV, packetsLost, pktRcvLoss); + STAT(RECV, packetsDropped, pktRcvDrop); + STAT(RECV, packetsRetransmitted, pktRcvRetrans); + STAT(RECV, packetsBelated, pktRcvBelated); + STAT(RECV, packetsFilterExtra, pktRcvFilterExtra); + STAT(RECV, packetsFilterSupply, pktRcvFilterSupply); + STAT(RECV, packetsFilterLoss, pktRcvFilterLoss); + STAT(RECV, bytes, byteRecv); + STAT(RECV, bytesUnique, byteRecvUnique); + STAT(RECV, bytesLost, byteRcvLoss); + STAT(RECV, bytesDropped, byteRcvDrop); + STAT(RECV, mbitRate, mbpsRecvRate); + + } +} g_SrtStatsTableInit (g_SrtStatsTable); + + +#undef STAT +#undef STATX + +string srt_json_cat_names [] = { + "", + "window", + "link", + "send", + "recv" +}; + class SrtStatsJson : public SrtStatsWriter { + static string keyspec(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(":)"; + } + public: - string WriteStats(int sid, const CBytePerfMon& mon) override - { + string WriteStats(int sid, const CBytePerfMon& mon) override + { std::ostringstream output; - output << "{"; - output << "\"sid\":" << sid << ","; - output << "\"time\":" << mon.msTimeStamp << ","; - output << "\"window\":{"; - output << "\"flow\":" << mon.pktFlowWindow << ","; - output << "\"congestion\":" << mon.pktCongestionWindow << ","; - output << "\"flight\":" << mon.pktFlightSize; - output << "},"; - output << "\"link\":{"; - output << "\"rtt\":" << mon.msRTT << ","; - output << "\"bandwidth\":" << mon.mbpsBandwidth << ","; - output << "\"maxBandwidth\":" << mon.mbpsMaxBW; - output << "},"; - output << "\"send\":{"; - output << "\"packets\":" << mon.pktSent << ","; - output << "\"packetsUnique\":" << mon.pktSentUnique << ","; - output << "\"packetsLost\":" << mon.pktSndLoss << ","; - output << "\"packetsDropped\":" << mon.pktSndDrop << ","; - output << "\"packetsRetransmitted\":" << mon.pktRetrans << ","; - output << "\"packetsFilterExtra\":" << mon.pktSndFilterExtra << ","; - output << "\"bytes\":" << mon.byteSent << ","; - output << "\"bytesUnique\":" << mon.byteSentUnique << ","; - output << "\"bytesDropped\":" << mon.byteSndDrop << ","; - output << "\"mbitRate\":" << mon.mbpsSendRate; - output << "},"; - output << "\"recv\": {"; - output << "\"packets\":" << mon.pktRecv << ","; - output << "\"packetsUnique\":" << mon.pktRecvUnique << ","; - output << "\"packetsLost\":" << mon.pktRcvLoss << ","; - output << "\"packetsDropped\":" << mon.pktRcvDrop << ","; - output << "\"packetsRetransmitted\":" << mon.pktRcvRetrans << ","; - output << "\"packetsBelated\":" << mon.pktRcvBelated << ","; - output << "\"packetsFilterExtra\":" << mon.pktRcvFilterExtra << ","; - output << "\"packetsFilterSupply\":" << mon.pktRcvFilterSupply << ","; - output << "\"packetsFilterLoss\":" << mon.pktRcvFilterLoss << ","; - output << "\"bytes\":" << mon.byteRecv << ","; - output << "\"bytesUnique\":" << mon.byteRecvUnique << ","; - output << "\"bytesLost\":" << mon.byteRcvLoss << ","; - output << "\"bytesDropped\":" << mon.byteRcvDrop << ","; - output << "\"mbitRate\":" << mon.mbpsRecvRate; - output << "}"; - output << "}" << endl; + static const string qt = R"(")"; + + string pretty_cr, pretty_tab; + if (Option("pretty")) + { + pretty_cr = "\n"; + pretty_tab = "\t"; + } + + SrtStatCat cat = SSC_GEN; + + // Do general manually + output << keyspec(srt_json_cat_names[cat]) << "{" << pretty_cr; + + // SID is displayed manually + output << pretty_tab << keyspec("sid") << sid; + + // Now continue with fields as specified in the table + for (auto& i: g_SrtStatsTable) + { + if (i->category == cat) + { + output << ","; // next item in same cat + output << pretty_cr; + output << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + else + { + if (cat != SSC_GEN) + { + // DO NOT close if general category, just + // enter the depth. + output << pretty_cr << pretty_tab << "}"; + } + cat = i->category; + output << ","; + output << pretty_cr; + if (cat != SSC_GEN) + output << pretty_tab; + + output << keyspec(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + + // Print the current field + output << keyspec(i->name); + output << qt; + i->PrintValue(output, mon); + output << qt; + } + + // Close the previous subcategory + if (cat != SSC_GEN) + { + output << pretty_cr << pretty_tab << "}" << pretty_cr; + } + + // Close the general category entity + output << "}," << pretty_cr << endl; + return output.str(); - } + } - string WriteBandwidth(double mbpsBandwidth) override + string WriteBandwidth(double mbpsBandwidth) override { std::ostringstream output; output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl; @@ -425,18 +524,19 @@ class SrtStatsCsv : public SrtStatsWriter #define HAS_PUT_TIME #endif std::ostringstream output; + + // Header if (!first_line_printed) { #ifdef HAS_PUT_TIME output << "Timepoint,"; #endif - output << "Time,SocketID,pktFlowWindow,pktCongestionWindow,pktFlightSize,"; - output << "msRTT,mbpsBandwidth,mbpsMaxBW,pktSent,pktSndLoss,pktSndDrop,"; - output << "pktRetrans,byteSent,byteSndDrop,mbpsSendRate,usPktSndPeriod,"; - output << "pktRecv,pktRcvLoss,pktRcvDrop,pktRcvRetrans,pktRcvBelated,"; - output << "byteRecv,byteRcvLoss,byteRcvDrop,mbpsRecvRate,RCVLATENCYms,"; - // Filter stats - output << "pktSndFilterExtra,pktRcvFilterExtra,pktRcvFilterSupply,pktRcvFilterLoss"; + output << "Time,SocketID"; + + for (auto& i: g_SrtStatsTable) + { + output << "," << i->longname; + } output << endl; first_line_printed = true; } @@ -444,7 +544,10 @@ class SrtStatsCsv : public SrtStatsWriter int int_len = sizeof rcv_latency; srt_getsockopt(sid, 0, SRTO_RCVLATENCY, &rcv_latency, &int_len); + // Values + #ifdef HAS_PUT_TIME + // HDR: Timepoint // Follows ISO 8601 auto print_timestamp = [&output]() { using namespace std; @@ -466,37 +569,16 @@ class SrtStatsCsv : public SrtStatsWriter print_timestamp(); #endif // HAS_PUT_TIME - output << mon.msTimeStamp << ","; - output << sid << ","; - output << mon.pktFlowWindow << ","; - output << mon.pktCongestionWindow << ","; - output << mon.pktFlightSize << ","; - output << mon.msRTT << ","; - output << mon.mbpsBandwidth << ","; - output << mon.mbpsMaxBW << ","; - output << mon.pktSent << ","; - output << mon.pktSndLoss << ","; - output << mon.pktSndDrop << ","; - output << mon.pktRetrans << ","; - output << mon.byteSent << ","; - output << mon.byteSndDrop << ","; - output << mon.mbpsSendRate << ","; - output << mon.usPktSndPeriod << ","; - output << mon.pktRecv << ","; - output << mon.pktRcvLoss << ","; - output << mon.pktRcvDrop << ","; - output << mon.pktRcvRetrans << ","; - output << mon.pktRcvBelated << ","; - output << mon.byteRecv << ","; - output << mon.byteRcvLoss << ","; - output << mon.byteRcvDrop << ","; - output << mon.mbpsRecvRate << ","; - output << rcv_latency << ","; - // Filter stats - output << mon.pktSndFilterExtra << ","; - output << mon.pktRcvFilterExtra << ","; - output << mon.pktRcvFilterSupply << ","; - output << mon.pktRcvFilterLoss; //<< ","; + // HDR: Time,SocketID + output << mon.msTimeStamp << "," << sid; + + // HDR: the loop of all values in g_SrtStatsTable + for (auto& i: g_SrtStatsTable) + { + output << ","; + i->PrintValue(output, mon); + } + output << endl; return output.str(); } @@ -555,8 +637,15 @@ shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat return nullptr; } -SrtStatsPrintFormat ParsePrintFormat(string pf) +SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) { + size_t havecomma = pf.find(','); + if (havecomma != string::npos) + { + w_extras = pf.substr(havecomma+1); + pf = pf.substr(0, havecomma); + } + if (pf == "default") return SRTSTATS_PROFMAT_2COLS; diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 1c8f4a0bb..044a68519 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -19,6 +19,7 @@ #include #include "netinet_any.h" +#include "utilities.h" #if _WIN32 @@ -314,7 +315,44 @@ enum SrtStatsPrintFormat SRTSTATS_PROFMAT_CSV }; -SrtStatsPrintFormat ParsePrintFormat(std::string pf); +SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras); + +enum SrtStatCat +{ + SSC_GEN, //< General + SSC_WINDOW, // flow/congestion window + SSC_LINK, //< Link data + SSC_SEND, //< Sending + SSC_RECV //< Receiving +}; + +struct SrtStatData +{ + SrtStatCat category; + std::string name; + std::string longname; + + SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} + + virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; +}; + +template +struct SrtStatDataType: public SrtStatData +{ + typedef TYPE CBytePerfMon::*pfield_t; + pfield_t pfield; + + SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field) + : SrtStatData (cat, name, longname), pfield(field) + { + } + + void PrintValue(std::ostream& str, const CBytePerfMon& mon) override + { + str << mon.*pfield; + } +}; class SrtStatsWriter { @@ -322,8 +360,30 @@ class SrtStatsWriter virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; virtual ~SrtStatsWriter() { }; + + void Option(const std::string& key, const std::string& val) + { + options[key] = val; + } + + bool Option(const std::string& key, std::string* rval = nullptr) + { + const std::string* out = map_getp(options, key); + if (!out) + return false; + + if (rval) + *rval = *out; + return true; + } + +protected: + std::map options; + }; +extern std::vector> g_SrtStatsTable; + std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index b1ec637e9..4288f351e 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -335,7 +335,8 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) cfg.stats_report = Option(params, o_statsrep); cfg.stats_out = Option(params, o_statsout); const string pf = Option(params, "default", o_statspf); - cfg.stats_pf = ParsePrintFormat(pf); + string pfext; + cfg.stats_pf = ParsePrintFormat(pf, (pfext)); if (cfg.stats_pf == SRTSTATS_PROFMAT_INVALID) { cfg.stats_pf = SRTSTATS_PROFMAT_2COLS; diff --git a/docs/srt-live-transmit.md b/docs/srt-live-transmit.md index ec29a2f1c..0a2dfb02d 100644 --- a/docs/srt-live-transmit.md +++ b/docs/srt-live-transmit.md @@ -366,7 +366,7 @@ shell (using **"** **"** quotes or backslash). - **-chunk, -c** - use given size of the buffer. The default size is 1456 bytes, which is the maximum payload size for a single SRT packet. - **-verbose, -v** - Display additional information on the standard output. Note that it's not allowed to be combined with output specified as **file://con**. - **-statsout**Ā - SRT statistics output: filename. Without this option specified, the statistics will be printed to the standard output. -- **-pf**, **-statspf**Ā - SRT statistics print format. Values:Ā json,Ā csv,Ā default. +- **-pf**, **-statspf**Ā - SRT statistics print format. Values:Ā json,Ā csv,Ā default. After a comma, options can be specified (e.g. "json,pretty"). - **-s**, **-stats**, **-stats-report-frequency**Ā - The frequency of SRT statistics collection, based on the number of packets. - **-loglevel** - lowest logging level for SRT, one of: *fatal, error, warning, note, debug* (default: *error*) - **-logfa, -lfa** - selected FAs in SRT to be logged (default: all are enabled). See the list of FAs running `-help:logging`. diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index daa3efc29..9c66dd30e 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -687,13 +687,26 @@ int main( int argc, char** argv ) #endif } - SrtStatsPrintFormat statspf = ParsePrintFormat(Option(params, "default", o_statspf)); + string pfextra; + SrtStatsPrintFormat statspf = ParsePrintFormat(Option(params, "default", o_statspf), (pfextra)); if (statspf == SRTSTATS_PROFMAT_INVALID) { cerr << "Invalid stats print format\n"; return 1; } transmit_stats_writer = SrtStatsWriterFactory(statspf); + if (pfextra != "") + { + vector options; + Split(pfextra, ',', back_inserter(options)); + for (auto& i: options) + { + vector klv; + Split(i, '=', back_inserter(klv)); + klv.resize(2); + transmit_stats_writer->Option(klv[0], klv[1]); + } + } // Options that require integer conversion size_t stoptime = Option(params, "0", o_stoptime); From 051760d8a82e00cda7a668f4f6ce119ee903cf6f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 29 Jan 2021 10:22:37 +0100 Subject: [PATCH 041/790] [core] Minor refactoring of backup CheckIdleTime --- srtcore/group.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 426ae67eb..27f0a094b 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2973,23 +2973,23 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // buffer gets empty so that we can make sure that KEEPALIVE will be the // really last sent for longer time. CUDT& u = w_d->ps->core(); - if (!is_zero(u.m_tsTmpActiveSince)) + if (is_zero(u.m_tsTmpActiveSince)) + return; + + CSndBuffer* b = u.m_pSndBuffer; + if (b && b->getCurrBufSize() == 0) { - CSndBuffer* b = u.m_pSndBuffer; - if (b && b->getCurrBufSize() == 0) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: FRESH IDLE LINK reached empty buffer - setting permanent and KEEPALIVE"); - u.m_tsTmpActiveSince = steady_clock::time_point(); - - // Send first immediate keepalive. The link is to be turn to IDLE - // now so nothing will be sent to it over time and it will start - // getting KEEPALIVES since now. Send the first one now to increase - // probability that the link will be recognized as IDLE on the - // reception side ASAP. - int32_t arg = 1; - w_d->ps->m_pUDT->sendCtrl(UMSG_KEEPALIVE, &arg); - } + HLOGC(gslog.Debug, + log << "grp/sendBackup: FRESH IDLE LINK reached empty buffer - setting permanent and KEEPALIVE"); + u.m_tsTmpActiveSince = steady_clock::time_point(); + + // Send first immediate keepalive. The link is to be turn to IDLE + // now so nothing will be sent to it over time and it will start + // getting KEEPALIVES since now. Send the first one now to increase + // probability that the link will be recognized as IDLE on the + // reception side ASAP. + int32_t arg = 1; + w_d->ps->m_pUDT->sendCtrl(UMSG_KEEPALIVE, &arg); } } From 85e30130a491ac83eeda185a9e3ac86b51ac335a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 29 Jan 2021 13:20:33 +0100 Subject: [PATCH 042/790] [apps] Fix virtual destructor for abstract struct SrtStatData (#1771) --- apps/apputil.cpp | 1 - apps/apputil.hpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 9e9ab4f2c..df3352626 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -405,7 +405,6 @@ struct SrtStatsTableInit STAT(RECV, bytesLost, byteRcvLoss); STAT(RECV, bytesDropped, byteRcvDrop); STAT(RECV, mbitRate, mbpsRecvRate); - } } g_SrtStatsTableInit (g_SrtStatsTable); diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 044a68519..59a171e58 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -333,6 +333,7 @@ struct SrtStatData std::string longname; SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} + virtual ~SrtStatData() {} virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; }; @@ -379,7 +380,6 @@ class SrtStatsWriter protected: std::map options; - }; extern std::vector> g_SrtStatsTable; From 17db0cb2ab2a3014d14336ea02aae4f388b318d1 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 29 Jan 2021 16:55:10 +0100 Subject: [PATCH 043/790] [tests] Added 1 p.c. tolerance for long waiting time (#1762) --- test/test_sync.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 713d190ff..47d23f7bc 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -314,7 +314,8 @@ TEST(SyncEvent, WaitFor) // - SyncEvent::wait_for( 50us) took 6us // - SyncEvent::wait_for(100us) took 4us if (on_timeout) { - EXPECT_GE(waittime_us, timeout_us); + const int tolerance = timeout_us/1000; + EXPECT_GE(waittime_us, timeout_us - tolerance); } #endif if (on_timeout) { From 481e7f75472265b22232794e5df5a6311d6bf518 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 1 Feb 2021 16:18:58 +0100 Subject: [PATCH 044/790] [core] Minor: renamed CUDT m_tsTmpActiveSince to m_tsFreshActivation (#1774) --- srtcore/core.cpp | 4 ++-- srtcore/core.h | 4 ++-- srtcore/group.cpp | 39 ++++++++++++++++++++------------------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 04abd3217..09ab9e00f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1556,7 +1556,7 @@ void CUDT::open() m_iReXmitCount = 1; m_tsUnstableSince = steady_clock::time_point(); - m_tsTmpActiveSince = steady_clock::time_point(); + m_tsFreshActivation = steady_clock::time_point(); m_iPktCount = 0; m_iLightACKCount = 1; @@ -5763,7 +5763,7 @@ void *CUDT::tsbpd(void *param) if (!is_zero(tsbpdtime)) { - const steady_clock::duration timediff = tsbpdtime - steady_clock::now(); + IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsbpdtime - steady_clock::now()); /* * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. diff --git a/srtcore/core.h b/srtcore/core.h index 26a28780a..153e4b5bf 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1189,8 +1189,8 @@ class CUDT static const size_t MAX_SID_LENGTH = 512; private: // Timers functions - time_point m_tsTmpActiveSince; // time since temporary activated, or 0 if not temporary activated - time_point m_tsUnstableSince; // time since unexpected ACK delay experienced, or 0 if link seems healthy + time_point m_tsFreshActivation; // time of fresh activation of the link, or 0 if past the activation phase or idle + time_point m_tsUnstableSince; // time since unexpected ACK delay experienced, or 0 if link seems healthy static const int BECAUSE_NO_REASON = 0, // NO BITS BECAUSE_ACK = 1 << 0, diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 27f0a094b..2bcc1e142 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2973,7 +2973,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // buffer gets empty so that we can make sure that KEEPALIVE will be the // really last sent for longer time. CUDT& u = w_d->ps->core(); - if (is_zero(u.m_tsTmpActiveSince)) + if (is_zero(u.m_tsFreshActivation)) return; CSndBuffer* b = u.m_pSndBuffer; @@ -2981,7 +2981,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) { HLOGC(gslog.Debug, log << "grp/sendBackup: FRESH IDLE LINK reached empty buffer - setting permanent and KEEPALIVE"); - u.m_tsTmpActiveSince = steady_clock::time_point(); + u.m_tsFreshActivation = steady_clock::time_point(); // Send first immediate keepalive. The link is to be turn to IDLE // now so nothing will be sent to it over time and it will start @@ -3015,7 +3015,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point log << "grp/sendBackup: CHECK STABLE: @" << d->id << ": TIMEDIFF {response= " << FormatDuration(currtime - u.m_tsLastRspTime) << " ACK=" << FormatDuration(currtime - u.m_tsLastRspAckTime) << " activation=" - << (!is_zero(u.m_tsTmpActiveSince) ? FormatDuration(currtime - u.m_tsTmpActiveSince) : "PAST") + << (!is_zero(u.m_tsFreshActivation) ? FormatDuration(currtime - u.m_tsFreshActivation) : "PAST") << " unstable=" << (!is_zero(u.m_tsUnstableSince) ? FormatDuration(currtime - u.m_tsUnstableSince) : "NEVER") << "}"); @@ -3026,7 +3026,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point const steady_clock::duration td_responsive = currtime - u.m_tsLastRspTime; bool check_stability = true; - if (!is_zero(u.m_tsTmpActiveSince) && u.m_tsTmpActiveSince < currtime) + if (!is_zero(u.m_tsFreshActivation) && u.m_tsFreshActivation < currtime) { // The link is temporary-activated. Calculate then since the activation time. @@ -3053,7 +3053,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point // As we DO have activation time, we need to check if there's at least // one ACK newer than activation, that is, td_acked < td_active - if (u.m_tsLastRspAckTime < u.m_tsTmpActiveSince) + if (u.m_tsLastRspAckTime < u.m_tsFreshActivation) { check_stability = false; HLOGC(gslog.Debug, @@ -3063,7 +3063,7 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point } else { - u.m_tsTmpActiveSince = steady_clock::time_point(); + u.m_tsFreshActivation = steady_clock::time_point(); } } @@ -3122,6 +3122,7 @@ bool CUDTGroup::sendBackup_CheckSendStatus(gli_t { bool none_succeeded = true; + // sending over this socket has succeeded if (stat != -1) { if (w_curseq == SRT_SEQNO_NONE) @@ -3380,7 +3381,7 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i if (d->sndstate != SRT_GST_RUNNING) { steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsTmpActiveSince = currtime; + d->ps->core().m_tsFreshActivation = currtime; HLOGC(gslog.Debug, log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno << " LINK ACTIVATED (pri: " << d->weight << ")."); @@ -3535,7 +3536,7 @@ struct FByOldestActive CUDT& x = a->ps->core(); CUDT& y = b->ps->core(); - return x.m_tsTmpActiveSince < y.m_tsTmpActiveSince; + return x.m_tsFreshActivation < y.m_tsFreshActivation; } }; @@ -3736,7 +3737,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, w_parallel.push_back(d); w_final_stat = stat; steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsTmpActiveSince = currtime; + d->ps->core().m_tsFreshActivation = currtime; d->sndstate = SRT_GST_RUNNING; w_none_succeeded = false; HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); @@ -3826,19 +3827,19 @@ void CUDTGroup::sendBackup_SilenceRedundantLinks(const vector& unstableLi } CUDT& ce = d->ps->core(); steady_clock::duration td(0); - if (!is_zero(ce.m_tsTmpActiveSince) && - count_microseconds(td = currtime - ce.m_tsTmpActiveSince) < ce.m_uOPT_StabilityTimeout) + if (!is_zero(ce.m_tsFreshActivation) && + count_microseconds(td = currtime - ce.m_tsFreshActivation) < ce.m_uOPT_StabilityTimeout) { HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " - << ce.m_uOPT_StabilityTimeout << "(stability timeout)"); + log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " + << ce.m_uOPT_StabilityTimeout << "(stability timeout)"); continue; } // Clear activation time because the link is no longer active! d->sndstate = SRT_GST_IDLE; - HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsTmpActiveSince)); - ce.m_tsTmpActiveSince = steady_clock::time_point(); + HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsFreshActivation)); + ce.m_tsFreshActivation = steady_clock::time_point(); } } @@ -3921,8 +3922,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) bool none_succeeded = true; // be pessimistic - // This should be added all sockets that are currently stable - // and sending was successful. Later, all but the one with highest + // All sockets that are currently stable and sending was successful + // should be added to the vector. Later, all but the one with highest // priority should remain active. vector parallel; @@ -4355,7 +4356,7 @@ void CUDTGroup::handleKeepalive(CUDTGroup::SocketData* gli) // which may result not only with exceeded stability timeout (which fortunately // isn't being measured in this case), but also with receiveing keepalive // (therefore we also don't reset the link to IDLE in the temporary activation period). - if (gli->sndstate == SRT_GST_RUNNING && is_zero(gli->ps->core().m_tsTmpActiveSince)) + if (gli->sndstate == SRT_GST_RUNNING && is_zero(gli->ps->core().m_tsFreshActivation)) { gli->sndstate = SRT_GST_IDLE; HLOGC(gslog.Debug, @@ -4375,7 +4376,7 @@ void CUDTGroup::internalKeepalive(SocketData* gli) { gli->rcvstate = SRT_GST_IDLE; // Prevent sending KEEPALIVE again in group-sending - gli->ps->core().m_tsTmpActiveSince = steady_clock::time_point(); + gli->ps->core().m_tsFreshActivation = steady_clock::time_point(); HLOGC(gslog.Debug, log << "GROUP: EXP-requested KEEPALIVE in @" << gli->id << " - link turning IDLE"); } } From a6a7a2021c575d33f9dbac72b10e63899026d789 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 1 Feb 2021 19:22:06 +0100 Subject: [PATCH 045/790] [core] Bonding: refactoring receiving function (#1763) --- srtcore/group.cpp | 433 ++++++++++++++++++++++------------------------ srtcore/group.h | 14 +- 2 files changed, 216 insertions(+), 231 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 2bcc1e142..bd34eb99e 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1918,6 +1918,188 @@ struct FLookupSocketWithEvent_LOCKED } }; +void CUDTGroup::recv_CollectAliveAndBroken(vector& alive, set& broken) +{ +#if ENABLE_HEAVY_LOGGING + std::ostringstream ds; + ds << "E(" << m_RcvEID << ") "; +#define HCLOG(expr) expr +#else +#define HCLOG(x) if (false) {} +#endif + + alive.reserve(m_Group.size()); + + HLOGC(grlog.Debug, log << "group/recv: Reviewing member sockets for polling"); + for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + if (gi->laststatus == SRTS_CONNECTING) + { + HCLOG(ds << "@" << gi->id << " "); + continue; // don't read over a failed or pending socket + } + + if (gi->laststatus >= SRTS_BROKEN) + { + broken.insert(gi->ps); + } + + if (broken.count(gi->ps)) + { + HCLOG(ds << "@" << gi->id << " "); + continue; + } + + if (gi->laststatus != SRTS_CONNECTED) + { + HCLOG(ds << "@" << gi->id << "laststatus) << "> "); + // Sockets in this state are ignored. We are waiting until it + // achieves CONNECTING state, then it's added to write. + // Or gets broken and closed in the next step. + continue; + } + + // Don't skip packets that are ahead because if we have a situation + // that all links are either "elephants" (do not report read readiness) + // and "kangaroos" (have already delivered an ahead packet) then + // omiting kangaroos will result in only elephants to be polled for + // reading. Due to the strict timing requirements and ensurance that + // TSBPD on every link will result in exactly the same delivery time + // for a packet of given sequence, having an elephant and kangaroo in + // one cage means that the elephant is simply a broken or half-broken + // link (the data are not delivered, but it will get repaired soon, + // enough for SRT to maintain the connection, but it will still drop + // packets that didn't arrive in time), in both cases it may + // potentially block the reading for an indefinite time, while + // simultaneously a kangaroo might be a link that got some packets + // dropped, but then it's still capable to deliver packets on time. + + // Note that gi->id might be a socket that was previously being polled + // on write, when it's attempting to connect, but now it's connected. + // This will update the socket with the new event set. + + alive.push_back(gi->ps); + HCLOG(ds << "@" << gi->id << "[READ] "); + } + + HLOGC(grlog.Debug, log << "group/recv: " << ds.str() << " --> EPOLL/SWAIT"); +#undef HCLOG +} + +vector CUDTGroup::recv_WaitForReadReady(const vector& aliveMembers, set& w_broken) +{ + if (aliveMembers.empty()) + { + LOGC(grlog.Error, log << "group/recv: all links broken"); + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } + + for (vector::const_iterator i = aliveMembers.begin(); i != aliveMembers.end(); ++i) + { + // NOT using the official srt_epoll_add_usock because this will do socket dispatching, + // which requires lock on m_GlobControlLock, while this lock cannot be applied without + // first unlocking m_GroupLock. + const int read_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; + CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); + } + + // Here we need to make an additional check. + // There might be a possibility that all sockets that + // were added to the reader group, are ahead. At least + // surely we don't have a situation that any link contains + // an ahead-read subsequent packet, because GroupCheckPacketAhead + // already handled that case. + // + // What we can have is that every link has: + // - no known seq position yet (is not registered in the position map yet) + // - the position equal to the latest delivered sequence + // - the ahead position + + // Now the situation is that we don't have any packets + // waiting for delivery so we need to wait for any to report one. + + // The non-blocking mode would need to simply check the readiness + // with only immediate report, and read-readiness would have to + // be done in background. + + // In blocking mode, use m_iRcvTimeOut, which's default value -1 + // means to block indefinitely, also in swait(). + // In non-blocking mode use 0, which means to always return immediately. + int timeout = m_bSynRecving ? m_iRcvTimeOut : 0; + int nready = 0; + // Poll on this descriptor until reading is available, indefinitely. + CEPoll::fmap_t sready; + + // GlobControlLock is required for dispatching the sockets. + // Therefore it must be applied only when GroupLock is off. + { + // This call may wait indefinite time, so GroupLock must be unlocked. + InvertedLock ung (m_GroupLock); + THREAD_PAUSED(); + nready = m_pGlobal->m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); + THREAD_RESUMED(); + + // HERE GlobControlLock is locked first, then GroupLock is applied back + enterCS(CUDT::s_UDTUnited.m_GlobControlLock); + } + // BOTH m_GlobControlLock AND m_GroupLock are locked here. + + HLOGC(grlog.Debug, log << "group/recv: " << nready << " RDY: " << DisplayEpollResults(sready)); + + if (nready == 0) + { + // GlobControlLock is applied manually, so unlock manually. + // GroupLock will be unlocked as per scope. + leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + // This can only happen when 0 is passed as timeout and none is ready. + // And 0 is passed only in non-blocking mode. So this is none ready in + // non-blocking mode. + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + + // Handle sockets of pending connection and with errors. + + // Nice to have something like: + + // broken = FilterIf(sready, [] (auto s) + // { return s.second == SRT_EPOLL_ERR && (auto cs = g->locateSocket(s.first, ERH_RETURN)) + // ? {cs, true} + // : {nullptr, false} + // }); + + FilterIf( + /*FROM*/ sready.begin(), + sready.end(), + /*TO*/ std::inserter(w_broken, w_broken.begin()), + /*VIA*/ FLookupSocketWithEvent_LOCKED(m_pGlobal, SRT_EPOLL_ERR)); + + + // If this set is empty, it won't roll even once, therefore output + // will be surely empty. This will be checked then same way as when + // reading from every socket resulted in error. + vector readReady; + readReady.reserve(sready.size()); + for (CEPoll::fmap_t::const_iterator i = sready.begin(); i != sready.end(); ++i) + { + if (i->second & SRT_EPOLL_ERR) + continue; // broken already + + if ((i->second & SRT_EPOLL_IN) == 0) + continue; // not ready for reading + + // Check if this socket is in aheads + // If so, don't read from it, wait until the ahead is flushed. + SRTSOCKET id = i->first; + CUDTSocket* ps = m_pGlobal->locateSocket_LOCKED(id); + if (ps) + readReady.push_back(ps); + } + + leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + + return readReady; +} + void CUDTGroup::updateReadState(SRTSOCKET /* not sure if needed */, int32_t sequence) { bool ready = false; @@ -2097,195 +2279,16 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // during the next time ahead check, after which they will become // horses. -#if ENABLE_HEAVY_LOGGING - std::ostringstream ds; - ds << "E(" << m_RcvEID << ") "; -#define HCLOG(expr) expr -#else -#define HCLOG(x) \ - if (false) \ - { \ - } -#endif - - bool still_alive = false; - size_t size = 0; + const size_t size = m_Group.size(); - // You can't lock the whole group for that - // action because this will result in a deadlock. // Prepare first the list of sockets to be added as connect-pending // and as read-ready, then unlock the group, and then add them to epoll. - vector read_ready; - vector connect_pending; - - { - HLOGC(grlog.Debug, log << "group/recv: Reviewing member sockets to epoll-add"); - for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) - { - ++size; // list::size loops over all elements anyway, so this hits two birds with one stone - if (gi->laststatus == SRTS_CONNECTING) - { - HCLOG(ds << "@" << gi->id << " "); - /* - connect_pending.push_back(gi->id); - */ - - continue; // don't read over a failed or pending socket - } - - if (gi->laststatus >= SRTS_BROKEN) - { - broken.insert(gi->ps); - } - - if (broken.count(gi->ps)) - { - HCLOG(ds << "@" << gi->id << " "); - continue; - } - - if (gi->laststatus != SRTS_CONNECTED) - { - HCLOG(ds << "@" << gi->id << "laststatus) << "> "); - // Sockets in this state are ignored. We are waiting until it - // achieves CONNECTING state, then it's added to write. - // Or gets broken and closed in the next step. - continue; - } - - still_alive = true; - - // Don't skip packets that are ahead because if we have a situation - // that all links are either "elephants" (do not report read readiness) - // and "kangaroos" (have already delivered an ahead packet) then - // omiting kangaroos will result in only elephants to be polled for - // reading. Due to the strict timing requirements and ensurance that - // TSBPD on every link will result in exactly the same delivery time - // for a packet of given sequence, having an elephant and kangaroo in - // one cage means that the elephant is simply a broken or half-broken - // link (the data are not delivered, but it will get repaired soon, - // enough for SRT to maintain the connection, but it will still drop - // packets that didn't arrive in time), in both cases it may - // potentially block the reading for an indefinite time, while - // simultaneously a kangaroo might be a link that got some packets - // dropped, but then it's still capable to deliver packets on time. - - // Note that gi->id might be a socket that was previously being polled - // on write, when it's attempting to connect, but now it's connected. - // This will update the socket with the new event set. - - read_ready.push_back(gi->ps); - HCLOG(ds << "@" << gi->id << "[READ] "); - } - } - - int read_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; - - /* Done at the connecting stage so that it won't be missed. - - int connect_modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - for (vector::iterator i = connect_pending.begin(); i != connect_pending.end(); ++i) - { - XXX This is wrong code; this should use the internal function and pass CUDTSocket* - epoll_add_usock_INTERNAL(m_RcvEID, i->second, &connect_modes); - } - - AND this below additionally for sockets that were so far pending connection, - will be now "upgraded" to readable sockets. The epoll adding function for a - socket that already is in the eid container will only change the poll flags, - but will not re-add it, that is, event reports that are both in old and new - flags will survive the operation. - - */ - - for (vector::iterator i = read_ready.begin(); i != read_ready.end(); ++i) - { - // NOT using the official srt_epoll_add_usock because this will do socket dispatching, - // which requires lock on m_GlobControlLock, while this lock cannot be applied without - // first unlocking m_GroupLock. - CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); - } - - HLOGC(grlog.Debug, log << "group/recv: " << ds.str() << " --> EPOLL/SWAIT"); -#undef HCLOG - - if (!still_alive) - { - LOGC(grlog.Error, log << "group/recv: all links broken"); - throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - } - - // Here we need to make an additional check. - // There might be a possibility that all sockets that - // were added to the reader group, are ahead. At least - // surely we don't have a situation that any link contains - // an ahead-read subsequent packet, because GroupCheckPacketAhead - // already handled that case. - // - // What we can have is that every link has: - // - no known seq position yet (is not registered in the position map yet) - // - the position equal to the latest delivered sequence - // - the ahead position - - // Now the situation is that we don't have any packets - // waiting for delivery so we need to wait for any to report one. - - // XXX We support blocking mode only at the moment. - // The non-blocking mode would need to simply check the readiness - // with only immediate report, and read-readiness would have to - // be done in background. - - // Poll on this descriptor until reading is available, indefinitely. - CEPoll::fmap_t sready; - - // In blocking mode, use m_iRcvTimeOut, which's default value -1 - // means to block indefinitely, also in swait(). - // In non-blocking mode use 0, which means to always return immediately. - int timeout = m_bSynRecving ? m_iRcvTimeOut : 0; - int nready = 0; - - // GlobControlLock is required for dispatching the sockets. - // Therefore it must be applied only when GroupLock is off. - { - // This call may wait indefinite time, so GroupLock must be unlocked. - InvertedLock ung (m_GroupLock); - THREAD_PAUSED(); - nready = m_pGlobal->m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); - THREAD_RESUMED(); - - // HERE GlobControlLock is locked first, then GroupLock is applied back - enterCS(CUDT::s_UDTUnited.m_GlobControlLock); - } - // BOTH m_GlobControlLock AND m_GroupLock are locked here. - - HLOGC(grlog.Debug, log << "group/recv: " << nready << " RDY: " << DisplayEpollResults(sready)); - - if (nready == 0) - { - // GlobControlLock is applied manually, so unlock manually. - // GroupLock will be unlocked as per scope. - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); - // This can only happen when 0 is passed as timeout and none is ready. - // And 0 is passed only in non-blocking mode. So this is none ready in - // non-blocking mode. - throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); - } - - // Handle sockets of pending connection and with errors. - - // Nice to have something like: - - // broken = FilterIf(sready, [] (auto s) - // { return s.second == SRT_EPOLL_ERR && (auto cs = g->locateSocket(s.first, ERH_RETURN)) - // ? {cs, true} - // : {nullptr, false} - // }); + vector aliveMembers; + recv_CollectAliveAndBroken(aliveMembers, broken); - FilterIf( - /*FROM*/ sready.begin(), - sready.end(), - /*TO*/ std::inserter(broken, broken.begin()), - /*VIA*/ FLookupSocketWithEvent_LOCKED(m_pGlobal, SRT_EPOLL_ERR)); + const vector ready_sockets = recv_WaitForReadReady(aliveMembers, broken); + // m_GlobControlLock lifted, m_GroupLock still locked. + // Now we can safely do this scoped way. // Ok, now we need to have some extra qualifications: // 1. If a socket has no registry yet, we read anyway, just @@ -2300,32 +2303,6 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) int32_t next_seq = m_RcvBaseSeqNo; - // If this set is empty, it won't roll even once, therefore output - // will be surely empty. This will be checked then same way as when - // reading from every socket resulted in error. - - vector ready_sockets; - - for (CEPoll::fmap_t::const_iterator i = sready.begin(); i != sready.end(); ++i) - { - if (i->second & SRT_EPOLL_ERR) - continue; // broken already - - if ((i->second & SRT_EPOLL_IN) == 0) - continue; // not ready for reading - - // Check if this socket is in aheads - // If so, don't read from it, wait until the ahead is flushed. - SRTSOCKET id = i->first; - CUDTSocket* ps = m_pGlobal->locateSocket_LOCKED(id); - if (ps) - ready_sockets.push_back(ps); - } - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); - - // m_GlobControlLock lifted, m_GroupLock still locked. - // Now we can safely do this scoped way. - if (m_bClosing) { HLOGC(gslog.Debug, log << "grp/sendBroadcast: GROUP CLOSED, ABANDONING"); @@ -2339,7 +2316,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // which is still applied here. So this will have to wait for this function to finish // (or block on swait, in which case the lock is lifted) anyway. - for (vector::iterator si = ready_sockets.begin(); si != ready_sockets.end(); ++si) + for (vector::const_iterator si = ready_sockets.begin(); si != ready_sockets.end(); ++si) { CUDTSocket* ps = *si; SRTSOCKET id = ps->m_SocketID; @@ -2354,7 +2331,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // x = 0: the socket should be ready to get the exactly next packet // x = 1: the case is already handled by GroupCheckPacketAhead. // x > 1: AHEAD. DO NOT READ. - int seqdiff = CSeqNo::seqcmp(p->mctrl.pktseq, m_RcvBaseSeqNo); + const int seqdiff = CSeqNo::seqcmp(p->mctrl.pktseq, m_RcvBaseSeqNo); if (seqdiff > 1) { HLOGC(grlog.Debug, @@ -3548,6 +3525,18 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, SRT_MSGCTRL& w_mc, CUDTException& w_cx) { +#if ENABLE_HEAVY_LOGGING + // Potential problem to be checked in developer mode + for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) + { + if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) + { + LOGC(gslog.Debug, + log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); + } + } +#endif + // In contradiction to broadcast sending, backup sending must check // the blocking state in total first. We need this information through // epoll because we didn't use all sockets to send the data hence the @@ -3759,22 +3748,8 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_SilenceRedundantLinks(const vector& unstableLinks, - vector& w_parallel) +void CUDTGroup::sendBackup_SilenceRedundantLinks(vector& w_parallel) { -#if ENABLE_HEAVY_LOGGING - // Potential problem to be checked in developer mode - for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) - { - if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) - { - LOGC(gslog.Debug, - log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); - } - } -#endif - - // The most important principle is to keep the data being sent constantly, // even if it means temporarily full redundancy. However, if you are certain // that you have multiple stable links running at the moment, SILENCE all but @@ -4101,7 +4076,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) sendBackup_RetryWaitBlocked(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); - sendBackup_SilenceRedundantLinks(unstableLinks, (parallel)); + sendBackup_SilenceRedundantLinks((parallel)); // (closing condition checked inside this call) if (none_succeeded) diff --git a/srtcore/group.h b/srtcore/group.h index e1853babe..afb05f0f9 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -326,8 +326,7 @@ class CUDTGroup bool& w_none_succeeded, SRT_MSGCTRL& w_mc, CUDTException& w_cx); - void sendBackup_SilenceRedundantLinks(const std::vector& unstable, - std::vector& w_parallel); + void sendBackup_SilenceRedundantLinks(std::vector& w_parallel); void send_CheckValidSockets(); @@ -666,6 +665,17 @@ class CUDTGroup ReadPos* checkPacketAhead(); + void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); + + /// The function polls alive member sockets and retrieves a list of read-ready. + /// [acquires lock for CUDT::s_UDTUnited.m_GlobControlLock] + /// [[using locked(m_GroupLock)]] temporally unlocks-locks internally + /// + /// @returns list of read-ready sockets + /// @throws CUDTException(MJ_CONNECTION, MN_NOCONN, 0) + /// @throws CUDTException(MJ_AGAIN, MN_RDAVAIL, 0) + std::vector recv_WaitForReadReady(const std::vector& aliveMembers, std::set& w_broken); + // This is the sequence number of a packet that has been previously // delivered. Initially it should be set to SRT_SEQNO_NONE so that the sequence read // from the first delivering socket will be taken as a good deal. From 524565f3046fa90922391a7954fe62164a8d42c1 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 2 Feb 2021 16:05:45 +0100 Subject: [PATCH 046/790] [apps] Added timepoint in json stats format (#1780) --- apps/apputil.cpp | 86 ++++++++++++++++++++++++++++++++---------------- apps/apputil.hpp | 3 ++ 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index df3352626..7d01bd9c4 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -351,6 +351,11 @@ string OptionHelpItem(const OptionName& o) // Stats module +// Note: std::put_time is supported only in GCC 5 and higher +#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) +#define HAS_PUT_TIME +#endif + template inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, TYPE CBytePerfMon::*field) @@ -420,9 +425,40 @@ string srt_json_cat_names [] = { "recv" }; +#ifdef HAS_PUT_TIME +// Follows ISO 8601 +std::string SrtStatsWriter::print_timestamp() +{ + using namespace std; + using namespace std::chrono; + + const auto systime_now = system_clock::now(); + const time_t time_now = system_clock::to_time_t(systime_now); + + std::ostringstream output; + + // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. + const tm tm_now = SysLocalTime(time_now); + output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); + const auto since_epoch = systime_now.time_since_epoch(); + const seconds s = duration_cast(since_epoch); + output << duration_cast(since_epoch - s).count(); + output << std::put_time(&tm_now, "%z"); + return output.str(); +} +#else + +// This is a stub. The error when not defining it would be too +// misleading, so this stub will work if someone mistakenly adds +// the item to the output format without checking that HAS_PUT_TIME. +string SrtStatsWriter::print_timestamp() +{ return ""; } +#endif // HAS_PUT_TIME + + class SrtStatsJson : public SrtStatsWriter { - static string keyspec(const string& name) + static string quotekey(const string& name) { if (name == "") return ""; @@ -430,6 +466,14 @@ class SrtStatsJson : public SrtStatsWriter return R"(")" + name + R"(":)"; } + static string quote(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(")"; + } + public: string WriteStats(int sid, const CBytePerfMon& mon) override { @@ -446,10 +490,17 @@ class SrtStatsJson : public SrtStatsWriter SrtStatCat cat = SSC_GEN; // Do general manually - output << keyspec(srt_json_cat_names[cat]) << "{" << pretty_cr; + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr; // SID is displayed manually - output << pretty_tab << keyspec("sid") << sid; + output << pretty_tab << quotekey("sid") << sid; + + // Extra Timepoint is also displayed manually +#ifdef HAS_PUT_TIME + // NOTE: still assumed SSC_GEN category + output << "," << pretty_cr << pretty_tab + << quotekey("timepoint") << quote(print_timestamp()); +#endif // Now continue with fields as specified in the table for (auto& i: g_SrtStatsTable) @@ -476,13 +527,13 @@ class SrtStatsJson : public SrtStatsWriter if (cat != SSC_GEN) output << pretty_tab; - output << keyspec(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; if (cat != SSC_GEN) output << pretty_tab; } // Print the current field - output << keyspec(i->name); + output << quotekey(i->name); output << qt; i->PrintValue(output, mon); output << qt; @@ -518,10 +569,6 @@ class SrtStatsCsv : public SrtStatsWriter string WriteStats(int sid, const CBytePerfMon& mon) override { - // Note: std::put_time is supported only in GCC 5 and higher -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) -#define HAS_PUT_TIME -#endif std::ostringstream output; // Header @@ -547,25 +594,7 @@ class SrtStatsCsv : public SrtStatsWriter #ifdef HAS_PUT_TIME // HDR: Timepoint - // Follows ISO 8601 - auto print_timestamp = [&output]() { - using namespace std; - using namespace std::chrono; - - const auto systime_now = system_clock::now(); - const time_t time_now = system_clock::to_time_t(systime_now); - - // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. - const tm tm_now = SysLocalTime(time_now); - output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); - const auto since_epoch = systime_now.time_since_epoch(); - const seconds s = duration_cast(since_epoch); - output << duration_cast(since_epoch - s).count(); - output << std::put_time(&tm_now, "%z"); - output << ","; - }; - - print_timestamp(); + output << print_timestamp() << ","; #endif // HAS_PUT_TIME // HDR: Time,SocketID @@ -657,4 +686,3 @@ SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) return SRTSTATS_PROFMAT_INVALID; } - diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 59a171e58..773b96b36 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -362,6 +362,9 @@ class SrtStatsWriter virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; virtual ~SrtStatsWriter() { }; + // Only if HAS_PUT_TIME. Specified in the imp file. + std::string print_timestamp(); + void Option(const std::string& key, const std::string& val) { options[key] = val; From 7d99b80bcc2193edae1c2bcc660e89b9266f6d26 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 3 Feb 2021 09:28:16 +0100 Subject: [PATCH 047/790] [core] Added handshake data check to prevent rogue handshakes (#1781) --- srtcore/api.h | 5 +- srtcore/core.cpp | 52 +++++++++++++++++---- srtcore/core.h | 2 +- srtcore/handshake.cpp | 13 ++++++ srtcore/handshake.h | 103 +++++++++++++++++++++--------------------- 5 files changed, 111 insertions(+), 64 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index 1e98d7c49..39ed9ce51 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -202,6 +202,9 @@ friend class CRendezvousQueue; CUDTUnited(); ~CUDTUnited(); + // Public constants + static const int32_t MAX_SOCKET_VAL = 1 << 29; // maximum value for a regular socket + public: enum ErrorHandling { ERH_RETURN, ERH_THROW, ERH_ABORT }; @@ -346,8 +349,6 @@ friend class CRendezvousQueue; srt::sync::Mutex m_IDLock; // used to synchronize ID generation - static const int32_t MAX_SOCKET_VAL = 1 << 29; // maximum value for a regular socket - SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 09ab9e00f..27dfc6662 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -238,6 +238,14 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent) m_pCache = NULL; + // This is in order to set it ANY kind of initial value, however + // this value should not be used when not connected and should be + // updated in the handshake. When this value is 0, it means that + // packets shall not be sent, as the other party doesn't have a + // room to receive and store it. Therefore this value should be + // overridden before any sending happens. + m_iFlowWindowSize = 0; + // Default congctl is "live". // Available builtin congctl: "file". // Other congctls can be registerred. @@ -4522,7 +4530,11 @@ EConnectStatus CUDT::processRendezvous( m_ConnReq.m_extension = needs_extension; // This must be done before prepareConnectionObjects(). - applyResponseSettings(); + if (!applyResponseSettings()) + { + LOGC(cnlog.Error, log << "processRendezvous: rogue peer"); + return CONN_REJECT; + } // This must be done before interpreting and creating HSv5 extensions. if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0)) @@ -4966,8 +4978,15 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti return postConnect(response, false, eout); } -void CUDT::applyResponseSettings() ATR_NOEXCEPT +bool CUDT::applyResponseSettings() ATR_NOEXCEPT { + if (!m_ConnRes.valid()) + { + LOGC(cnlog.Error, log << "applyResponseSettings: ROGUE HANDSHAKE - rejecting"); + m_RejectReason = SRT_REJ_ROGUE; + return false; + } + // Re-configure according to the negotiated values. m_iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; @@ -4977,7 +4996,7 @@ void CUDT::applyResponseSettings() ATR_NOEXCEPT setInitialRcvSeq(m_iPeerISN); - m_iRcvCurrPhySeqNo = m_ConnRes.m_iISN - 1; + m_iRcvCurrPhySeqNo = CSeqNo::decseq(m_ConnRes.m_iISN); m_PeerID = m_ConnRes.m_iID; memcpy((m_piSelfIP), m_ConnRes.m_piPeerIP, sizeof m_piSelfIP); @@ -4985,6 +5004,8 @@ void CUDT::applyResponseSettings() ATR_NOEXCEPT log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " isn=" << m_ConnRes.m_iISN << " peerID=" << m_ConnRes.m_iID); + + return true; } EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT @@ -5007,24 +5028,28 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE // // Currently just this function must be called always BEFORE prepareConnectionObjects // everywhere except acceptAndRespond(). - applyResponseSettings(); + bool ok = applyResponseSettings(); // This will actually be done also in rendezvous HSv4, // however in this case the HSREQ extension will not be attached, // so it will simply go the "old way". - bool ok = prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout); + // (&&: skip if failed already) + ok = ok && prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout); + // May happen that 'response' contains a data packet that was sent in rendezvous mode. // In this situation the interpretation of handshake was already done earlier. - if (ok && response.isControl()) + ok = ok && response.isControl(); + ok = ok && interpretSrtHandshake(m_ConnRes, response, 0, 0); + + if (!ok) { - ok = interpretSrtHandshake(m_ConnRes, response, 0, 0); - if (!ok && eout) + if (eout) { *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0); } - } - if (!ok) // m_RejectReason already set + // m_RejectReason already set return CONN_REJECT; + } } bool have_group = false; @@ -10758,6 +10783,13 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // set in the above INDUCTION, in the HS_VERSION_SRT1 // should also contain extra data. + if (!hs.valid()) + { + LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); + m_RejectReason = SRT_REJ_ROGUE; + return SRT_REJ_ROGUE; + } + HLOGC(cnlog.Debug, log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); if (hs.m_iCookie != cookie_val) diff --git a/srtcore/core.h b/srtcore/core.h index 153e4b5bf..b5cd59d08 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -546,7 +546,7 @@ class CUDT SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket &response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; - void applyResponseSettings() ATR_NOEXCEPT; + SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr); SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index e3726ea9e..ff3a30ebd 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -52,6 +52,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "udt.h" +#include "api.h" #include "core.h" #include "handshake.h" #include "utilities.h" @@ -186,6 +187,18 @@ string CHandShake::RdvStateStr(CHandShake::RendezvousState s) } #endif +bool CHandShake::valid() +{ + if (m_iVersion < CUDT::HS_VERSION_UDT4 + || m_iISN < 0 || m_iISN >= CSeqNo::m_iMaxSeqNo + || m_iMSS < 32 + || m_iFlightFlagSize < 2 + || m_iID >= CUDTUnited::MAX_SOCKET_VAL) + return false; + + return true; +} + string CHandShake::show() { ostringstream so; diff --git a/srtcore/handshake.h b/srtcore/handshake.h index e674fce33..ca6184b5d 100644 --- a/srtcore/handshake.h +++ b/srtcore/handshake.h @@ -285,20 +285,20 @@ inline std::string RequestTypeStr(UDTRequestType) { return ""; } class CHandShake { public: - CHandShake(); + CHandShake(); - int store_to(char* buf, size_t& size); - int load_from(const char* buf, size_t size); + int store_to(char* buf, size_t& size); + int load_from(const char* buf, size_t size); public: - // This is the size of SERIALIZED handshake. - // Might be defined as simply sizeof(CHandShake), but the - // enum values would have to be forced as int32_t, which is only - // available in C++11. Theoretically they are all 32-bit, but - // such a statement is not reliable and not portable. - static const size_t m_iContentSize = 48; // Size of hand shake data + // This is the size of SERIALIZED handshake. + // Might be defined as simply sizeof(CHandShake), but the + // enum values would have to be forced as int32_t, which is only + // available in C++11. Theoretically they are all 32-bit, but + // such a statement is not reliable and not portable. + static const size_t m_iContentSize = 48; // Size of hand shake data - // Extension flags + // Extension flags static const int32_t HS_EXT_HSREQ = BIT(0); static const int32_t HS_EXT_KMREQ = BIT(1); @@ -310,50 +310,51 @@ class CHandShake int32_t flags() { return m_iType; } public: - int32_t m_iVersion; // UDT version (HS_VERSION_* symbols) - int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags - int32_t m_iISN; // random initial sequence number - int32_t m_iMSS; // maximum segment size - int32_t m_iFlightFlagSize; // flow control window size - UDTRequestType m_iReqType; // handshake stage - int32_t m_iID; // socket ID - int32_t m_iCookie; // cookie - uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to - - bool m_extension; - - std::string show(); - -// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way). -// -// The WAVING state is the very initial state of the rendezvous connection and restored after the -// connection is closed. -// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible -// situations are: -// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE -// - "parallel arrangement" both parties transit to ATTENTION -// -// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first -// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same -// time, on machines with exactly the same performance and all things preceding the message sending -// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if -// the clients have started at different times, the one who started first sends a message and the -// system of the receiver buffers this message even before the client binds the port for enough long -// time so that it outlasts also the possible second, repeated waveahand. -enum RendezvousState -{ - RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events. - RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer. - RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION]. - RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE]. - RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state. - RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT. -}; + int32_t m_iVersion; // UDT version (HS_VERSION_* symbols) + int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags + int32_t m_iISN; // random initial sequence number + int32_t m_iMSS; // maximum segment size + int32_t m_iFlightFlagSize; // flow control window size + UDTRequestType m_iReqType; // handshake stage + int32_t m_iID; // socket ID + int32_t m_iCookie; // cookie + uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to + + bool m_extension; + + bool valid(); + std::string show(); + + // The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way). + // + // The WAVING state is the very initial state of the rendezvous connection and restored after the + // connection is closed. + // The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible + // situations are: + // - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE + // - "parallel arrangement" both parties transit to ATTENTION + // + // Parallel arrangement is a "virtually impossible" case, in which both parties must send the first + // URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same + // time, on machines with exactly the same performance and all things preceding the message sending + // have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if + // the clients have started at different times, the one who started first sends a message and the + // system of the receiver buffers this message even before the client binds the port for enough long + // time so that it outlasts also the possible second, repeated waveahand. + enum RendezvousState + { + RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events. + RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer. + RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION]. + RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE]. + RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state. + RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT. + }; #if ENABLE_LOGGING -static std::string RdvStateStr(RendezvousState s); + static std::string RdvStateStr(RendezvousState s); #else -static std::string RdvStateStr(RendezvousState) { return ""; } + static std::string RdvStateStr(RendezvousState) { return ""; } #endif }; From 64edcf6efde6145f5918ba00744a1a31fa24d5aa Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 3 Feb 2021 11:23:55 +0100 Subject: [PATCH 048/790] [apps] Fix too premature stopped reading in srt-file-transmit (#1785) --- apps/srt-file-transmit.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 521060cb5..5fb856c98 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -557,7 +557,14 @@ bool DoDownload(UriParser& us, string directory, string filename, } } break; + + // No need to do any special action in case of broken. + // The app will just try to read and in worst case it will + // get an error. case SRTS_BROKEN: + cerr << "Connection closed, reading buffer remains\n"; + break; + case SRTS_NONEXIST: case SRTS_CLOSED: { @@ -601,7 +608,7 @@ bool DoDownload(UriParser& us, string directory, string filename, if (n == 0) { result = true; - cerr << "Download COMPLETE."; + cerr << "Download COMPLETE.\n"; break; } From 5ec84d2fd53f7ab0b92eb4a7bd486991c42c1f9b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 2 Feb 2021 17:06:27 +0100 Subject: [PATCH 049/790] [core] Fixed faulty packet drop by a group. m_RcvBaseSeqNo must be updated only when a packet is read. However, it could have been updated also when only checked. --- srtcore/group.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index bd34eb99e..c76b8d6c4 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2215,6 +2215,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) << ": " << BufferStamp(&pos->packet[0], pos->packet.size())); memcpy(buf, &pos->packet[0], pos->packet.size()); fillGroupData((w_mc), pos->mctrl); + m_RcvBaseSeqNo = pos->mctrl.pktseq; len = pos->packet.size(); pos->packet.clear(); @@ -2385,7 +2386,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } if (stat == 0) { - HLOGC(grlog.Debug, log << "group/recv: SPURIOUS epoll, ignoring"); + HLOGC(grlog.Debug, log << "group/recv @" << id << ": SPURIOUS epoll, ignoring"); // This is returned in case of "again". In case of errors, we have SRT_ERROR. // Do not treat this as spurious, just stop reading. break; @@ -2434,7 +2435,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { // Now we can safely check it. - int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo); + const int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo); if (seqdiff <= 0) { @@ -2550,7 +2551,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) m_RcvBaseSeqNo = next_seq; } - ReadPos* pos = checkPacketAhead(); + const ReadPos* pos = checkPacketAhead(); if (!pos) { // Don't clear the read-readinsess state if you have a packet ahead because @@ -2658,7 +2659,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // clears the possibility of having aheads at all. // XXX Research if this is possible at all; if it isn't, then don't waste time on // looking for it. - ReadPos* pos = checkPacketAhead(); + const ReadPos* pos = checkPacketAhead(); if (!pos) { // Don't clear the read-readinsess state if you have a packet ahead because @@ -2700,8 +2701,6 @@ CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead() if (seqdiff == 1) { // The very next packet. Return it. - // XXX SETTING THIS ONE IS PROBABLY A BUG. - m_RcvBaseSeqNo = a.mctrl.pktseq; HLOGC(grlog.Debug, log << "group/recv: Base %" << m_RcvBaseSeqNo << " ahead delivery POSSIBLE %" << a.mctrl.pktseq << "#" << a.mctrl.msgno << " from @" << i->first << ")"); From 44503cd3a259fc5572e04b4c9187df29361b121d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 4 Feb 2021 09:53:56 +0100 Subject: [PATCH 050/790] [apps] Fixed hangup when exitting on interrupt (#1787) --- testing/srt-test-live.cpp | 16 ++++++++-------- testing/testmedia.cpp | 25 ++++++++++++++++++++++++- testing/testmedia.hpp | 1 + 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 9c66dd30e..5977b52b7 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -101,14 +101,11 @@ struct AlarmExit: public std::runtime_error } }; -volatile bool int_state = false; volatile bool timer_state = false; void OnINT_ForceExit(int) { cerr << "\n-------- REQUESTED INTERRUPT!\n"; - int_state = true; - if ( transmit_throw_on_interrupt ) - throw ForcedExit("Requested exception interrupt"); + transmit_int_state = true; } std::string g_interrupt_reason; @@ -116,7 +113,7 @@ std::string g_interrupt_reason; void OnAlarm_Interrupt(int) { cerr << "\n---------- INTERRUPT ON TIMEOUT: hang on " << g_interrupt_reason << "!\n"; - int_state = false; // JIC + transmit_int_state = true; // JIC timer_state = true; throw AlarmExit("Watchdog bites hangup"); } @@ -419,6 +416,8 @@ int main( int argc, char** argv ) if ( !SysInitializeNetwork() ) throw std::runtime_error("Can't initialize network!"); + srt_startup(); + // Symmetrically, this does a cleanup; put into a local destructor to ensure that // it's called regardless of how this function returns. struct NetworkCleanup @@ -426,6 +425,7 @@ int main( int argc, char** argv ) ~NetworkCleanup() { SysCleanupNetwork(); + srt_cleanup(); } } cleanupobj; @@ -835,7 +835,7 @@ int main( int argc, char** argv ) } catch(std::exception& x) { - if (::int_state) + if (::transmit_int_state) { // The application was terminated by SIGINT or SIGTERM. // Don't print anything, just exit gently like ffmpeg. @@ -939,7 +939,7 @@ int main( int argc, char** argv ) Verb() << "sent"; - if ( int_state ) + if (::transmit_int_state) { Verror() << "\n (interrupted on request)"; break; @@ -985,7 +985,7 @@ int main( int argc, char** argv ) { Verror() << "Exit on timeout."; } - else if (::int_state) + else if (::transmit_int_state) { Verror() << "Exit on interrupt."; // Do nothing. diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 93145c9d9..274207c65 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -49,6 +49,7 @@ using srt_logging::MemberStatusStr; #endif volatile bool transmit_throw_on_interrupt = false; +volatile bool transmit_int_state = false; int transmit_bw_report = 0; unsigned transmit_stats_report = 0; size_t transmit_chunk_size = SRT_LIVE_DEF_PLSIZE; @@ -530,8 +531,15 @@ void SrtCommon::AcceptNewClient() int len = 2; SRTSOCKET ready[2]; - if (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, -1, 0, 0, 0, 0) == -1) + while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == -1) + { + if (::transmit_int_state) + Error("srt_epoll_wait for srt_accept: interrupt"); + + if (srt_getlasterror(NULL) == SRT_ETIMEOUT) + continue; Error("srt_epoll_wait(srt_conn_epoll)"); + } Verb() << "[EPOLL: " << len << " sockets] " << VerbNoEOL; } @@ -785,6 +793,11 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) if (m_timeout) result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout); + else + { + int timeout = 1000; + result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout); + } if (result == -1) return result; } @@ -2274,6 +2287,9 @@ MediaPacket SrtSource::Read(size_t chunk) } #endif + if (::transmit_int_state) + Error("srt_recvmsg2: interrupted"); + ::transmit_throw_on_interrupt = true; stat = srt_recvmsg2(m_sock, data.data(), chunk, &mctrl); ::transmit_throw_on_interrupt = false; @@ -2324,6 +2340,13 @@ MediaPacket SrtSource::Read(size_t chunk) // If was -1, then passthru. } } + else + { + // In blocking mode it uses a minimum of 1s timeout, + // and continues only if interrupt not requested. + if (srt_getlasterror(NULL) == SRT_EASYNCRCV) + continue; + } Error("srt_recvmsg2"); } diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index ad70f6967..b251f140b 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -22,6 +22,7 @@ extern srt_listen_callback_fn* transmit_accept_hook_fn; extern void* transmit_accept_hook_op; +extern volatile bool transmit_int_state; extern std::shared_ptr transmit_stats_writer; From 8845473e3422fb25c15a0d61ad6a18664ddba594 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 4 Feb 2021 14:47:12 +0100 Subject: [PATCH 051/790] [core] Fixed packet drop when reading from members (#1784) TSBPD thread of a member socket might not yet trigger epoll read-ready event, while it is potentially ready. If another member signals read-readiness ahead of the current position it will result in a packet drop by a group. --- srtcore/group.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c76b8d6c4..d9973f129 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2078,21 +2078,27 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // will be surely empty. This will be checked then same way as when // reading from every socket resulted in error. vector readReady; - readReady.reserve(sready.size()); - for (CEPoll::fmap_t::const_iterator i = sready.begin(); i != sready.end(); ++i) + readReady.reserve(aliveMembers.size()); + for (vector::const_iterator sockiter = aliveMembers.begin(); sockiter != aliveMembers.end(); ++sockiter) { - if (i->second & SRT_EPOLL_ERR) - continue; // broken already + CUDTSocket* sock = *sockiter; + const CEPoll::fmap_t::const_iterator ready_iter = sready.find(sock->m_SocketID); + if (ready_iter != sready.end()) + { + if (ready_iter->second & SRT_EPOLL_ERR) + continue; // broken already - if ((i->second & SRT_EPOLL_IN) == 0) - continue; // not ready for reading + if ((ready_iter->second & SRT_EPOLL_IN) == 0) + continue; // not ready for reading - // Check if this socket is in aheads - // If so, don't read from it, wait until the ahead is flushed. - SRTSOCKET id = i->first; - CUDTSocket* ps = m_pGlobal->locateSocket_LOCKED(id); - if (ps) - readReady.push_back(ps); + readReady.push_back(*sockiter); + } + else if (sock->core().m_pRcvBuffer->isRcvDataReady()) + { + // No read-readiness reported by epoll, but probably missed or not yet handled + // as the receiver buffer is read-ready. + readReady.push_back(sock); + } } leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); From c89995b7988ddc96345cef8145f329feff089e5a Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 4 Feb 2021 14:48:49 +0100 Subject: [PATCH 052/790] [core] FEC: Aligned NOTDONE and SUCCESS result of hanging as acceptable (#1790) --- srtcore/fec.cpp | 7 +++++-- srtcore/fec.h | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 9e25087c5..598bdd0e0 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -772,6 +772,9 @@ bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) static string hangname [] = {"SUCCESS", "PAST", "CRAZY", "NOT-DONE"}; #endif + // Required for EHangStatus + using namespace std::rel_ops; + EHangStatus okh = HANG_NOTDONE; if (!isfec.col) // == regular packet or FEC/ROW { @@ -783,7 +786,7 @@ bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) << " RESULT=" << hangname[okh] << " IRRECOVERABLE: " << Printable(irrecover_row)); } - if (okh != HANG_SUCCESS) + if (okh > HANG_SUCCESS) { // Just informative. LOGC(pflog.Warn, log << "FEC/H: rebuilding/hanging FAILED."); @@ -806,7 +809,7 @@ bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) << " IRRECOVERABLE: " << Printable(irrecover_col)); } - if (okv != HANG_SUCCESS) + if (okv > HANG_SUCCESS) { // Just informative. LOGC(pflog.Warn, log << "FEC/V: rebuilding/hanging FAILED."); diff --git a/srtcore/fec.h b/srtcore/fec.h index 795de658f..ef150ce5d 100644 --- a/srtcore/fec.h +++ b/srtcore/fec.h @@ -196,12 +196,17 @@ class FECFilterBuiltin: public SrtPacketFilterBase enum EHangStatus { + HANG_NOTDONE, HANG_SUCCESS, HANG_PAST, - HANG_CRAZY, - HANG_NOTDONE + HANG_CRAZY }; + friend bool operator <(FECFilterBuiltin::EHangStatus a, FECFilterBuiltin::EHangStatus b) + { + return int(a) < int(b); + } + EHangStatus HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover); EHangStatus HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover); void ClipControlPacket(Group& g, const CPacket& pkt); From dcd62caf2114309de626d1a6e8cc0de7fa3ef349 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 5 Feb 2021 08:26:59 +0100 Subject: [PATCH 053/790] [core] Revised pre-bind option restrictions (#1759) * Fixed documentation on option restrictions * Changed live-mode options to be pre-restricted * Added connecting state for fixed pre options. Fixed pre-bind restriction on SRTO_IPV6ONLY * Fixed wrong return code for pre-bind ipv6only * Fixed doc for bindtodevice changed to pre-bind --- docs/APISocketOptions.md | 494 ++++++++++++++++++++------------------- srtcore/core.cpp | 35 +-- 2 files changed, 271 insertions(+), 258 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 5e7313526..878a0ae17 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -124,18 +124,28 @@ their characteristics according to the following legend: is empty, it's an option derived from UDT. "Version 0.0.0" is the oldest version of SRT ever created and put into use. -2. **Binding**: Defines limitation on setting the option. The field is empty if the option +2. **Restrict**: Defines restrictions on setting the option. The field is empty if the option is not settable (see **Dir** column): - - `pre`: A connecting socket (both as caller and rendezvous) must be set -prior to calling `srt_connect()` or `srt_bind()` and never changed thereafter. -A listener socket should be set to "listening" and it will be -derived by every socket returned by `srt_accept()`. + - `pre-bind`: The option cannot be altered on a socket that is already bound (by calling +`srt_bind()` or any other function doing this, including automatic binding when trying to +connect, as well as accepted sockets). - - `post`: This flag can be changed any time, including after the socket is -connected (as well as on an accepted socket). Setting this flag on a listening -socket is effective only on that socket itself. Note though that there are some -post-bound options that have important meaning when set prior to connecting. + - `pre`: Like pre-bind, but only for a connected socket (including an accepted socket). If +an option was set on a listener socket, it will be set the same on a socket returned by +`srt_accept()`. + + - `post`: The option is unrestricted and can be altered at any time, including when the +socket is connected, as well as on an accepted socket. The setting of this flag on a listening +socket is usually derived by the accepted socket, but this isn't a rule for all options. +Note though that there are some unrestricted options that have an important meaning when +set prior to connecting (different one than for a connected socket). + + **NOTE**: The `pre-bind` characteristic applies exclusively to options that: + + - Change the behavior and functionality of the `srt_bind` call + - Concern or set an option on the internally used UDP socket + - Concern any kind of resource used by the multiplexer 3. **Type**: The data type of the option (see above). @@ -188,74 +198,74 @@ The + marker can only coexist with GS. Possible specifications are: The following table lists SRT socket options in alphabetical order. Option details are given further below. -| Option Name | Since | Binding | Type | Units | Default | Range | Dir |Entity | -| :----------------------------------------------------- | :---: | :-----: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| -| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | -| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | -| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | -| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | -| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | -| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | -| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | -| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | pre | `int32_t` | enum | | | R | S | -| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre | `int32_t` | | (system) | 0..255 | RW | GSD | -| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre | `int32_t` | hops | (system) | 1..255 | RW | GSD | -| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre | `int32_t` | | (system) | -1..1 | RW | GSD | -| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | -| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000000 | 0.. | RW | GSD | -| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | -| [`SRTO_LINGER`](#SRTO_LINGER) | | pre | `linger` | s | on, 180 | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | -| [`SRTO_MAXBW`](#SRTO_MAXBW) | 1.0.5 | pre | `int64_t` | B/s | -1 | -1.. | RW | GSD | -| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | -| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | -| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | -| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | -| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | -| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | -| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | -| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | -| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | -| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre | `bool` | | true | | RW | GSD | -| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | pre | `int32_t` | ms | * | -1.. | W | GSD+ | -| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | post | `int32_t` | enum | | | R | S | -| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | -| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | -| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre | `int32_t` | bytes | 65536 | * | RW | GSD+ | -| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | +| Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | +| :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| +| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | +| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | +| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | post | `int32_t` | ms | 3000 | 0.. | W | GSD+ | +| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | +| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | +| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | +| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | +| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | +| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | +| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | +| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | +| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | +| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | +| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | +| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000000 | 0.. | RW | GSD | +| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | +| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | +| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | +| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | +| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | +| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | +| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | +| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | +| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | +| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | +| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | +| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | +| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | +| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | +| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | +| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | +| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | +| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | +| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | +| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | +| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | +| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions #### SRTO_BINDTODEVICE -| OptName | Since | Binding | Type | Units | Default | Range | Dir |Entity| -| --------------------- | ----- | ------- | -------- | ------ | -------- | ------ |-----|------| -| `SRTO_BINDTODEVICE` | 1.4.2 | pre | `string` | | | | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir |Entity| +| --------------------- | ----- | -------- | -------- | ------ | -------- | ------ |-----|------| +| `SRTO_BINDTODEVICE` | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | Refers to the `SO_BINDTODEVICE` system socket option for `SOL_SOCKET` level. This effectively limits the packets received by this socket to only those @@ -276,9 +286,9 @@ for a process that runs as root. Otherwise the function that applies the setting #### SRTO_CONGESTION -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_CONGESTION` | 1.3.0 | pre | `string` | | "live" | * | W | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_CONGESTION` | 1.3.0 | pre | `string` | | "live" | * | W | S | The type of congestion controller used for the transmission for that socket. @@ -298,9 +308,9 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST #### SRTO_CONNTIMEO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ------------------ | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_CONNTIMEO` | 1.1.2 | pre | `int32_t` | msec | 3000 | 0.. | W | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_CONNTIMEO` | 1.1.2 | post | `int32_t` | msec | 3000 | 0.. | W | GSD+ | Connect timeout. This option applies to the caller and rendezvous connection modes. For the rendezvous mode (see `SRTO_RENDEZVOUS`) the effective connection timeout @@ -312,8 +322,8 @@ will be 10 times the value set with `SRTO_CONNTIMEO`. #### SRTO_DRIFTTRACER -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | | `SRTO_DRIFTTRACER`| 1.4.2 | post | `bool` | | true | | RW | GSD | Enables or disables time drift tracer (receiver). @@ -324,9 +334,9 @@ Enables or disables time drift tracer (receiver). #### SRTO_ENFORCEDENCRYPTION -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_ENFORCEDENCRYPTION` | 1.3.2 | pre | `bool` | | true | | W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_ENFORCEDENCRYPTION` | 1.3.2 | pre | `bool` | | true | | W | GSD | This option enforces that both connection parties have the same passphrase set, or both do not set the passphrase, otherwise the connection is rejected. @@ -369,9 +379,9 @@ on the caller side. #### SRTO_EVENT -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_EVENT` | | | `int32_t` | flags | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_EVENT` | | | `int32_t` | flags | | | R | S | Returns bit flags set according to the current active events on the socket. @@ -384,9 +394,9 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of #### SRTO_FC -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | Flight Flag Size (maximum number of bytes that can be sent without being acknowledged) @@ -397,9 +407,9 @@ being acknowledged) #### SRTO_GROUPCONNECT -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_GROUPCONNECT` | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_GROUPCONNECT` | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | When this flag is set to 1 on a listener socket, it allows this socket to accept group connections. When set to the default 0, group connections will be @@ -423,9 +433,9 @@ function will return the group, not this socket ID. #### SRTO_GROUPSTABTIMEO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_GROUPSTABTIMEO` | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_GROUPSTABTIMEO` | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | This setting is used for groups of type `SRT_GTYPE_BACKUP`. It defines the stability timeout, which is the maximum interval between two consecutive packets retrieved from @@ -466,9 +476,9 @@ option, as the default value of it is way above any sensible value of #### SRTO_GROUPTYPE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_GROUPTYPE` | 1.5.0 | pre | `int32_t` | enum | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_GROUPTYPE` | 1.5.0 | | `int32_t` | enum | | | R | S | This option is read-only and it is intended to be called inside the listener callback handler (see `srt_listen_callback`). Possible values are defined in @@ -485,9 +495,9 @@ context than inside the listener callback handler, the value is undefined. #### SRTO_INPUTBW -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ---------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_INPUTBW` | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_INPUTBW` | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | This option is effective only if `SRTO_MAXBW` is set to 0 (relative). It controls the maximum bandwidth together with `SRTO_OHEADBW` option according @@ -505,9 +515,9 @@ and keep the default 25% value for `SRTO_OHEADBW`*. #### SRTO_IPTOS -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ---------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_IPTOS` | 1.0.5 | pre | `int32_t` | | (system) | 0..255 | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_IPTOS` | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | IPv4 Type of Service (see `IP_TOS` option for IP) or IPv6 Traffic Class (see `IPV6_TCLASS` of IPv6) depending on socket address family. Applies to sender only. @@ -523,9 +533,9 @@ and the actual value for connected sockets. #### SRTO_IPTTL -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ---------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_IPTTL` | 1.0.5 | pre | `int32_t` | hops | (system) | 1..255 | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_IPTTL` | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | IPv4 Time To Live (see `IP_TTL` option for IP) or IPv6 unicast hops (see `IPV6_UNICAST_HOPS` for IPv6) depending on socket address family. Applies to sender only. @@ -541,9 +551,9 @@ and the actual value for connected sockets. #### SRTO_IPV6ONLY -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ---------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_IPV6ONLY` | 1.4.0 | pre | `int32_t` | | (system) | -1..1 | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_IPV6ONLY` | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | Set system socket flag `IPV6_V6ONLY`. When set to 0 a listening socket binding an IPv6 address accepts also IPv4 clients (their addresses will be formatted as @@ -556,9 +566,9 @@ platform default value is used. #### SRTO_ISN -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ---------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_ISN` | 1.3.0 | | `int32_t` | | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_ISN` | 1.3.0 | | `int32_t` | | | | R | S | The value of the ISN (Initial Sequence Number), which is the first sequence number put on the first UDP packets sent that are carrying an SRT data payload. @@ -573,9 +583,9 @@ used in any regular development.* #### SRTO_KMPREANNOUNCE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | The interval (defined in packets) between when a new Stream Encrypting Key (SEK) is sent and when switchover occurs. This value also applies to the @@ -603,9 +613,9 @@ not yet arrived at the receiver). #### SRTO_KMREFRESHRATE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000000| 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000000| 0.. | RW | GSD | The number of packets to be transmitted after which the Stream Encryption Key (SEK), used to encrypt packets, will be switched to the new one. Note that @@ -623,9 +633,9 @@ might still be in flight, or packets that have to be retransmitted. #### SRTO_KMSTATE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMSTATE` | 1.0.2 | | `int32_t` | enum | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_KMSTATE` | 1.0.2 | | `int32_t` | enum | | | R | S | Keying Material state. This is a legacy option that is equivalent to `SRTO_SNDKMSTATE`, if the socket has set `SRTO_SENDER` to true, and @@ -641,9 +651,9 @@ for more details. #### SRTO_LATENCY -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_LATENCY` | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `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. @@ -660,9 +670,9 @@ be sender and receiver at the same time, and `SRTO_SENDER` became redundant. #### SRTO_LINGER -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_LINGER` | | pre | `linger` | s | on, 180 | 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_LINGER` | | pre | `linger` | s | on, 180 | 0.. | RW | GSD | Linger time on close (see [SO\_LINGER](http://man7.org/linux/man-pages/man7/socket.7.html)). @@ -674,9 +684,9 @@ Linger time on close (see [SO\_LINGER](http://man7.org/linux/man-pages/man7/sock #### SRTO_LOSSMAXTTL -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_LOSSMAXTTL` | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_LOSSMAXTTL` | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | The value up to which the *Reorder Tolerance* may grow. The *Reorder Tolerance* is the number of packets that must follow the experienced "gap" in sequence numbers @@ -695,9 +705,9 @@ By default this value is set to 0, which means that this mechanism is off. #### SRTO_MAXBW -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MAXBW` | 1.0.5 | pre | `int64_t` | B/s | -1 | -1.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_MAXBW` | 1.0.5 | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | Maximum send bandwidth: @@ -719,9 +729,9 @@ therefore the default -1 remains even in live mode. #### SRTO_MESSAGEAPI -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MESSAGEAPI` | 1.3.0 | pre | `bool` | | true | | W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_MESSAGEAPI` | 1.3.0 | pre | `bool` | | true | | W | GSD | When set, this socket uses the Message API[\*], otherwise it uses the Stream API. Note that in live mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option) only the @@ -758,9 +768,9 @@ SCTP protocol. #### SRTO_MINVERSION -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | The minimum SRT version that is required from the peer. A connection to a peer that does not satisfy the minimum version requirement will be rejected. @@ -772,9 +782,9 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. #### SRTO_MSS -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_MSS` | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | Maximum Segment Size. Used for buffer allocation and rate calculation using packet counter assuming fully filled packets. Each party can set its own MSS @@ -793,9 +803,9 @@ UDP and SRT headers* #### SRTO_NAKREPORT -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_NAKREPORT` | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_NAKREPORT` | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | When set to true, every report for a detected loss will be repeated when the timeout for the expected retransmission of this loss has expired and the @@ -810,9 +820,9 @@ The default is true for Live mode, and false for File mode (see [`SRTO_TRANSTYPE #### SRTO_OHEADBW -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_OHEADBW` | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_OHEADBW` | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | Recovery bandwidth overhead above input rate (see [`SRTO_INPUTBW`](#SRTO_INPUTBW)), in percentage of the input rate. It is effective only if `SRTO_MAXBW` is set to 0. @@ -840,9 +850,9 @@ and break quickly at any rise in packet loss. #### SRTO_PACKETFILTER -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PACKETFILTER` | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PACKETFILTER` | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | Set up the packet filter. The string must match appropriate syntax for packet filter setup. @@ -862,9 +872,9 @@ For details, see [Packet Filtering & FEC](packet-filtering-and-fec.md). #### SRTO_PASSPHRASE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PASSPHRASE` | 0.0.0 | pre | `string` | | "" |[10..79]| W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PASSPHRASE` | 0.0.0 | pre | `string` | | "" |[10..79]| W | GSD | Sets the passphrase for encryption. This enables encryption on this party (or disables it, if an empty passphrase is passed). The password must be minimum @@ -891,9 +901,9 @@ encrypted connection, they have to simply set the same passphrase. #### SRTO_PAYLOADSIZE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | Sets the maximum declared size of a single call to sending function in Live mode. When set to 0, there's no limit for a single sending call. @@ -910,9 +920,9 @@ For File mode: Default value is 0 and it's recommended not to be changed. #### SRTO_PBKEYLEN -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PBKEYLEN` | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PBKEYLEN` | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | Encryption key length. @@ -966,9 +976,9 @@ undefined behavior: #### SRTO_PEERIDLETIMEO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PEERIDLETIMEO` | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PEERIDLETIMEO` | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | The maximum time in `[ms]` to wait until another packet is received from a peer since the last such packet reception. If this time is passed, the connection is @@ -980,9 +990,9 @@ considered broken on timeout. #### SRTO_PEERLATENCY -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PEERLATENCY` | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PEERLATENCY` | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | The latency value (as described in [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY)) provided by the sender side as a minimum value for the receiver. @@ -1001,9 +1011,9 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). #### SRTO_PEERVERSION -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PEERVERSION` | 1.1.0 | | `int32_t` | * | | | R | GS | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | +| `SRTO_PEERVERSION` | 1.1.0 | | `int32_t` | * | | | R | GS | SRT version used by the peer. The value 0 is returned if not connected, SRT handshake not yet performed (HSv4 only), or if peer is not SRT. @@ -1015,9 +1025,9 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. #### SRTO_RCVBUF -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVBUF` | | pre | `int32_t` | bytes | 8192 bufs | * | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVBUF` | | pre-bind | `int32_t` | bytes | 8192 bufs | * | RW | GSD+ | Receive Buffer Size, in bytes. Note, however, that the internal setting of this value is in the number of buffers, each one of size equal to SRT payload size, @@ -1035,9 +1045,9 @@ than the Flight Flag size). #### SRTO_RCVDATA -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVDATA` | | | `int32_t` | pkts | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVDATA` | | | `int32_t` | pkts | | | R | S | Size of the available data in the receive buffer. @@ -1047,9 +1057,9 @@ Size of the available data in the receive buffer. #### SRTO_RCVKMSTATE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVKMSTATE` | 1.2.0 | | `int32_t` | enum | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVKMSTATE` | 1.2.0 | | `int32_t` | enum | | | R | S | KM state on the agent side when it's a receiver. @@ -1061,9 +1071,9 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). #### SRTO_RCVLATENCY -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVLATENCY` | 1.3.0 | pre | `int32_t` | ms | * | 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVLATENCY` | 1.3.0 | pre | `int32_t` | ms | * | 0.. | RW | GSD | The latency value in the receiving direction of the socket. This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. @@ -1103,9 +1113,9 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). #### SRTO_RCVSYN -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVSYN` | | post | `bool` | | true | | RW | GSI | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVSYN` | | post | `bool` | | true | | RW | GSI | When true, sets blocking mode on reading function when it's not ready to perform the operation. When false ("non-blocking mode"), the reading function @@ -1140,9 +1150,9 @@ derived from the socket of which the group is a member). #### SRTO_RCVTIMEO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RCVTIMEO` | | post | `int32_t` | ms | -1 | -1, 0..| RW | GSI | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RCVTIMEO` | | post | `int32_t` | ms | -1 | -1, 0..| RW | GSI | Limits the time up to which the receiving operation will block (see [`SRTO_RCVSYN`](#SRTO_RCVSYN) for details), such that when this time is exceeded, @@ -1154,9 +1164,9 @@ it will behave as if in "non-blocking mode". The -1 value means no time limit. #### SRTO_RENDEZVOUS -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_RENDEZVOUS` | | pre | `bool` | | false | | RW | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_RENDEZVOUS` | | pre | `bool` | | false | | RW | S | Use Rendezvous connection mode (both sides must set this and both must use the procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one another. @@ -1167,9 +1177,9 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot #### SRTO_RETRANSMITALGO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | ------- | --------- | ------ | ------- | ------ | --- | ------ | -| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | +| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | Retransmission algorithm to use (SENDER option): @@ -1190,9 +1200,9 @@ Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). #### SRTO_REUSEADDR -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_REUSEADDR` | | pre | `bool` | | true | | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_REUSEADDR` | | pre-bind | `bool` | | true | | RW | GSD | When true, allows the SRT socket to use the binding address used already by another SRT socket in the same application. Note that SRT socket uses an @@ -1217,9 +1227,9 @@ its address.* #### SRTO_SENDER -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | ---------- | ------ | --- | ------ | -| `SRTO_SENDER` | 1.0.4 | pre | `bool` | | false | | W | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | +| `SRTO_SENDER` | 1.0.4 | pre | `bool` | | false | | W | S | Set sender side. The side that sets this flag is expected to be a sender. This flag is only required when communicating with a receiver that uses SRT version @@ -1233,9 +1243,9 @@ work. Setting `SRTO_MINVERSION` to 1.3.0 is therefore recommended. #### SRTO_SNDBUF -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDBUF` | | pre | `int32_t` | bytes |8192 bufs | * | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDBUF` | | pre-bind | `int32_t` | bytes |8192 bufs | * | RW | GSD+ | Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. @@ -1245,9 +1255,9 @@ Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. #### SRTO_SNDDATA -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDDATA` | | | `int32_t` | pkts | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDDATA` | | | `int32_t` | pkts | | | R | S | Size of the unacknowledged data in send buffer. @@ -1257,9 +1267,9 @@ Size of the unacknowledged data in send buffer. #### SRTO_SNDDROPDELAY -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDDROPDELAY` | 1.3.2 | pre | `int32_t` | ms | * | -1.. | W | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDDROPDELAY` | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | Sets an extra delay before `TLPKTDROP` is triggered on the data sender. This delay is added to the default drop delay time interval value. Keep in mind @@ -1284,9 +1294,9 @@ always when requested). #### SRTO_SNDKMSTATE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDKMSTATE` | 1.2.0 | post | `int32_t` | enum | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDKMSTATE` | 1.2.0 | | `int32_t` | enum | | | R | S | Peer KM state on receiver side for `SRTO_KMSTATE` @@ -1298,9 +1308,9 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). #### SRTO_SNDSYN -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDSYN` | | post | `bool` | | true | | RW | GSI | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDSYN` | | post | `bool` | | true | | RW | GSI | When true, sets blocking mode on writing function when it's not ready to perform the operation. When false ("non-blocking mode"), the writing function @@ -1324,9 +1334,9 @@ but will have no effect on the listener socket itself. #### SRTO_SNDTIMEO -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_SNDTIMEO` | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_SNDTIMEO` | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | limit the time up to which the sending operation will block (see `SRTO_SNDSYN` for details), so when this time is exceeded, it will behave as @@ -1338,9 +1348,9 @@ if in "non-blocking mode". The -1 value means no time limit. #### SRTO_STATE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_STATE` | | | `int32_t` | enum | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_STATE` | | | `int32_t` | enum | | | R | S | Returns the current socket state, same as `srt_getsockstate`. @@ -1350,9 +1360,9 @@ Returns the current socket state, same as `srt_getsockstate`. #### SRTO_STREAMID -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| -------------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_STREAMID` | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_STREAMID` | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | - A string that can be set on the socket prior to connecting. The listener side will be able to retrieve this stream ID from the socket that is returned from @@ -1375,9 +1385,9 @@ roles in the connection. #### SRTO_TLPKTDROP -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_TLPKTDROP` | 1.0.6 | pre | `bool` | | * | | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_TLPKTDROP` | 1.0.6 | pre | `bool` | | * | | RW | GSD | Too-late Packet Drop. When enabled on receiver, it skips missing packets that have not been delivered in time and delivers the subsequent packets to the @@ -1394,9 +1404,9 @@ enabled in sender if receiver supports it. #### SRTO_TRANSTYPE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_TRANSTYPE` | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE`| \* | W | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_TRANSTYPE` | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE`| \* | W | S | Sets the transmission type for the socket, in particular, setting this option sets multiple other parameters to their default values as required for a @@ -1410,9 +1420,9 @@ Values defined by enum `SRT_TRANSTYPE` (see above for possible values) #### SRTO_TSBPDMODE -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_TSBPDMODE` | 0.0.0 | pre | `bool` | | \* | | W | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_TSBPDMODE` | 0.0.0 | pre | `bool` | | \* | | W | S | When true, use Timestamp-based Packet Delivery mode. In this mode the packet's time is assigned at the sending time (or allowed to be predefined), @@ -1428,9 +1438,9 @@ the application. #### SRTO_UDP_RCVBUF -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_UDP_RCVBUF` | | pre | `int32_t` | bytes | 8192 bufs | * | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_UDP_RCVBUF` | | pre-bind | `int32_t` | bytes | 8192 bufs | * | RW | GSD+ | UDP Socket Receive Buffer Size. Configured in bytes, maintained in packets based on MSS value. Receive buffer must not be greater than FC size. @@ -1441,9 +1451,9 @@ based on MSS value. Receive buffer must not be greater than FC size. #### SRTO_UDP_SNDBUF -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_UDP_SNDBUF` | | pre | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_UDP_SNDBUF` | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | UDP Socket Send Buffer Size. Configured in bytes, maintained in packets based on `SRTO_MSS` value. @@ -1454,9 +1464,9 @@ on `SRTO_MSS` value. #### SRTO_VERSION -| OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | -| ----------------- | ----- | ------- | ---------- | ------- | --------- | ------ | --- | ------ | -| `SRTO_VERSION` | 1.1.0 | | `int32_t` | | | | R | S | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | +| `SRTO_VERSION` | 1.1.0 | | `int32_t` | | | | R | S | Local SRT version. This is the highest local version supported if not connected, or the highest version supported by the peer if connected. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 27dfc6662..ee631a818 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -517,6 +517,9 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) case SRTO_BINDTODEVICE: #ifdef SRT_ENABLE_BINDTODEVICE + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + { string val; if (optlen == -1) @@ -564,37 +567,37 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_TSBPDMODE: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_bOPT_TsbPd = cast_optval(optval, optlen); break; case SRTO_LATENCY: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_iOPT_TsbPdDelay = cast_optval(optval, optlen); m_iOPT_PeerTsbPdDelay = cast_optval(optval); break; case SRTO_RCVLATENCY: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_iOPT_TsbPdDelay = cast_optval(optval, optlen); break; case SRTO_PEERLATENCY: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_iOPT_PeerTsbPdDelay = cast_optval(optval, optlen); break; case SRTO_TLPKTDROP: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_bOPT_TLPktDrop = cast_optval(optval, optlen); break; @@ -690,8 +693,8 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_NAKREPORT: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); m_bRcvNakReport = cast_optval(optval, optlen); break; @@ -797,8 +800,8 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_TRANSTYPE: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); // XXX Note that here the configuration for SRTT_LIVE // is the same as DEFAULT VALUES for these fields set @@ -907,8 +910,8 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_IPV6ONLY: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); m_iIpV6Only = cast_optval(optval, optlen); break; From df55d7029acc3b2aaa74eb2b34c57927465bd6be Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Feb 2021 16:08:41 +0100 Subject: [PATCH 054/790] [build] Using xenial dist in Travis with Java 11 (#1786) Resolves SonarCloud upload issue. --- .travis.yml | 8 +++----- apps/srt-file-transmit.cpp | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index d369deb8d..82c16d99c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: cpp -dist: trusty -jdk: - - oraclejdk11 +dist: xenial addons: apt: @@ -70,7 +68,7 @@ script: - if [ "$TRAVIS_COMPILER" == "x86_64-w64-mingw32-g++" ]; then export CC="x86_64-w64-mingw32-gcc"; export CXX="x86_64-w64-mingw32-g++"; - cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="ON" -DUSE_OPENSSL_PC="OFF" -DOPENSSL_ROOT_DIR="$PWD/openssl" -DCMAKE_SYSTEM_NAME="Windows"; + cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="OFF" -DUSE_OPENSSL_PC="OFF" -DOPENSSL_ROOT_DIR="$PWD/openssl" -DCMAKE_SYSTEM_NAME="Windows"; elif [ "$TRAVIS_OS_NAME" == "linux" ]; then cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="ON"; elif [ "$TRAVIS_OS_NAME" == "osx" ]; then @@ -90,8 +88,8 @@ script: fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; + source ./scripts/collect-gcov.sh; fi - - source ./scripts/collect-gcov.sh - if (( "$RUN_CODECOV" )); then bash <(curl -s https://codecov.io/bash); fi diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 5fb856c98..9c1ec82f4 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -449,7 +449,7 @@ bool DoUpload(UriParser& ut, string path, string filename, } Verb() << "Sending buffer still: bytes=" << bytes << " blocks=" << blocks; - this_thread::sleep_for(chrono::milliseconds(250)); + srt::sync::this_thread::sleep_for(srt::sync::milliseconds_from(250)); } } From 2e9c1c70ef90b50fef244c0226d2357633e957bb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Feb 2021 16:12:26 +0100 Subject: [PATCH 055/790] [core] Added group receiver drop log warning (#1761) --- srtcore/group.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d9973f129..e48207287 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2640,6 +2640,9 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) if (jump > 0) { m_stats.recvDrop.UpdateTimes(jump, avgRcvPacketSize()); + LOGC(grlog.Warn, + log << "@" << m_GroupID << " GROUP RCV-DROPPED " << jump << " packet(s): seqno %" + << m_RcvBaseSeqNo << " to %" << slowest_kangaroo->second.mctrl.pktseq); } m_RcvBaseSeqNo = slowest_kangaroo->second.mctrl.pktseq; From 662db726b78dd2a7de4d4ed0ca33feedaf9e3f9c Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Sun, 7 Feb 2021 16:51:12 +0800 Subject: [PATCH 056/790] [apps] Traverse srt_options by const reference --- apps/socketoptions.cpp | 4 ++-- apps/transmitmedia.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/socketoptions.cpp b/apps/socketoptions.cpp index 31825ecb6..68df7291e 100644 --- a/apps/socketoptions.cpp +++ b/apps/socketoptions.cpp @@ -102,7 +102,7 @@ SocketOption::Mode SrtConfigurePre(SRTSOCKET socket, string host, map options, vector dummy; vector& fails = failures ? *failures : dummy; - for (auto o: srt_options) + for (const auto &o: srt_options) { if ( o.binding == SocketOption::POST && options.count(o.name) ) { diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index b024eb6d8..fb4b13748 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -302,7 +302,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) SrtConfigurePost(sock, m_options); - for (auto o: srt_options) + for (const auto &o: srt_options) { if ( o.binding == SocketOption::POST && m_options.count(o.name) ) { From 76567593fc1e10d694957fab5e7ee56dbdc916d3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 8 Feb 2021 10:07:45 +0100 Subject: [PATCH 057/790] [core] Runtime link stability timeout for main/backup (#1775) --- CMakeLists.txt | 1 + docs/APISocketOptions.md | 2 + srtcore/core.cpp | 2 +- srtcore/core.h | 6 +- srtcore/group.cpp | 191 ++++++++++++++++++++++++--------------- 5 files changed, 127 insertions(+), 75 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5f06d3f7..626bbc7e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ endforeach() # SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */ # SRT_DEBUG_TLPKTDROP_DROPSEQ 1 # SRT_DEBUG_SNDQ_HIGHRATE 1 +# SRT_DEBUG_BONDING_STATES 1 # SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */ # option defaults diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 878a0ae17..aa34f4fb4 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -437,6 +437,8 @@ function will return the group, not this socket ID. | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | | `SRTO_GROUPSTABTIMEO` | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | +**Not in use at the moment. Is to be repurposed in SRT v1.4.3!** + This setting is used for groups of type `SRT_GTYPE_BACKUP`. It defines the stability timeout, which is the maximum interval between two consecutive packets retrieved from the peer on the currently active link. These two packets can be of any type, diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ee631a818..85bd2f369 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11175,7 +11175,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea * (keepalive fix) * duB: * It seems there is confusion of the direction of the Response here. - * LastRspTime is supposed to be when receiving (data/ctrl) from peer + * lastRspTime is supposed to be when receiving (data/ctrl) from peer * as shown in processCtrl and processData, * Here we set because we sent something? * diff --git a/srtcore/core.h b/srtcore/core.h index b5cd59d08..8c0a1a85e 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -374,9 +374,12 @@ class CUDT bool isOPT_TsbPd() const { return m_bOPT_TsbPd; } int RTT() const { return m_iRTT; } + int RTTVar() const { return m_iRTTVar; } int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); + srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } + srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } int flowWindowSize() const { return m_iFlowWindowSize; } @@ -385,7 +388,8 @@ class CUDT int64_t maxBandwidth() const { return m_llMaxBW; } int MSS() const { return m_iMSS; } - uint32_t latency_us() const {return m_iTsbPdDelay_ms*1000; } + uint32_t peerLatency_us() const {return m_iPeerTsbPdDelay_ms * 1000; } + int peerIdleTimeout_ms() const { return m_iOPT_PeerIdleTimeout; } size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } size_t OPT_PayloadSize() const { return m_zOPT_ExpPayloadSize; } int sndLossLength() { return m_pSndLossList->getLossLength(); } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index e48207287..15e7e7ace 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2978,6 +2978,111 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) } } +#if SRT_DEBUG_BONDING_STATES +class StabilityTracer +{ +public: + StabilityTracer() + { + } + + ~StabilityTracer() + { + srt::sync::ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint32_t activation_period_us, + int64_t stability_tmo_us, const std::string& state, uint16_t weight) + { + srt::sync::ScopedLock lck(m_mtx); + create_file(); + + m_fout << srt::sync::FormatTime(currtime) << ","; + m_fout << u.id() << ","; + m_fout << weight << ","; + m_fout << u.peerLatency_us() << ","; + m_fout << u.RTT() << ","; + m_fout << u.RTTVar() << ","; + m_fout << stability_tmo_us << ","; + m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; + m_fout << state << ","; + m_fout << (srt::sync::is_zero(u.freshActivationStart()) ? -1 : (count_microseconds(currtime - u.freshActivationStart()))) << ","; + m_fout << activation_period_us << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + //srt::sync::ScopedLock lck(m_mtx); + m_fout << "Timepoint,SocketID,weight,usLatency,usRTT,usRTTVar,usStabilityTimeout,usSinceLastResp,State,usSinceActivation,usActivationPeriod\n"; + } + + void create_file() + { + if (m_fout) + return; + + std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); + str_tnow.resize(str_tnow.size() - 6); // remove trailing ' [SYS]' part + while (str_tnow.find(':') != std::string::npos) { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "stability_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; +}; + +StabilityTracer s_stab_trace; +#endif + +/// TODO: Remove 'weight' parameter? Only needed for logging. +/// @retval 1 - link is identified as stable +/// @retval 0 - link state remains unchanged (too early to identify, still in activation phase) +/// @retval -1 - link is identified as unstable +static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint16_t weight) +{ + const uint32_t latency_us = u.peerLatency_us(); + const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. + const int64_t initial_stabtout_us = max(min_stability_us, latency_us); + const int64_t activation_period_us = initial_stabtout_us + 5 * CUDT::COMM_SYN_INTERVAL_US; + + // RTT and RTTVar values are still being refined during activation period, + // therefore the dymanic timeout should not be used in activation phase. + const bool is_activation_phase = !is_zero(u.freshActivationStart()) + && (count_microseconds(currtime - u.freshActivationStart()) <= activation_period_us); + + const int64_t stability_tout_us = is_activation_phase + ? initial_stabtout_us // activation phase + : min(max(min_stability_us, 2 * u.RTT() + 4 * u.RTTVar()), latency_us); + + const steady_clock::time_point last_rsp = max(u.freshActivationStart(), u.lastRspTime()); + const steady_clock::duration td_response = currtime - last_rsp; + if (count_microseconds(td_response) > stability_tout_us) + { +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION-UNSTABLE" : "UNSTABLE", weight); +#endif + return -1; + } + + // u.lastRspTime() > currtime is alwats true due to the very first check above in this function +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION" : "STABLE", weight); +#endif + return is_activation_phase ? 0 : 1; +} + + // [[using locked(this->m_GroupLock)]] bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point currtime) { @@ -2994,8 +3099,6 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point // negative value is relatively easy, while introducing a mutex would only add a // deadlock risk and performance degradation. - bool is_stable = true; - HLOGC(gslog.Debug, log << "grp/sendBackup: CHECK STABLE: @" << d->id << ": TIMEDIFF {response= " << FormatDuration(currtime - u.m_tsLastRspTime) @@ -3005,89 +3108,33 @@ bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point << (!is_zero(u.m_tsUnstableSince) ? FormatDuration(currtime - u.m_tsUnstableSince) : "NEVER") << "}"); - if (currtime > u.m_tsLastRspTime) - { - // The last response predates the start of this function, look at the difference - const steady_clock::duration td_responsive = currtime - u.m_tsLastRspTime; - bool check_stability = true; - - if (!is_zero(u.m_tsFreshActivation) && u.m_tsFreshActivation < currtime) - { - // The link is temporary-activated. Calculate then since the activation time. - - // Check the last received ACK time first. This time is initialized with 'now' - // at the CUDT::open call, so you can't count on the trap zero time here, but - // it's still possible to check if activation time predates the ACK time. Things - // are here in the following possible order: - // - // - ACK time (old because defined at open) - // - Response time (old because the time of received handshake or keepalive counts) - // ... long time nothing ... - // - Activation time. - // - // If we have this situation, we have to wait for at least one ACK that is - // newer than activation time. However, if in this situation we have a fresh - // response, that is: - // - // - ACK time - // ... - // - Activation time - // - Response time (because a Keepalive had a caprice to come accidentally after sending) - // - // We still wait for a situation that there's at least one ACK that is newer than activation. + const int is_stable = sendBackup_CheckRunningLinkStable(u, currtime, d->weight); - // As we DO have activation time, we need to check if there's at least - // one ACK newer than activation, that is, td_acked < td_active - if (u.m_tsLastRspAckTime < u.m_tsFreshActivation) - { - check_stability = false; - HLOGC(gslog.Debug, - log << "grp/sendBackup: link @" << d->id - << " activated after ACK, " - "not checking for stability"); - } - else - { - u.m_tsFreshActivation = steady_clock::time_point(); - } - } - - if (check_stability && count_microseconds(td_responsive) > m_uOPT_StabilityTimeout) - { - if (is_zero(u.m_tsUnstableSince)) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket NEW UNSTABLE: @" << d->id << " last heard " - << FormatDuration(td_responsive) << " > " << m_uOPT_StabilityTimeout - << " (stability timeout)"); - // The link seems to have missed two ACKs already. - // Qualify this link as unstable - // Notify that it has been seen so since now - u.m_tsUnstableSince = currtime; - } - - is_stable = false; - } - } - - if (is_stable) + if (is_stable >= 0) { - // If stability is ok, but unstable-since was set before, reset it. HLOGC(gslog.Debug, log << "grp/sendBackup: link STABLE: @" << d->id << (!is_zero(u.m_tsUnstableSince) ? " - RESTORED" : " - CONTINUED") << ", state RUNNING - will send a payload"); u.m_tsUnstableSince = steady_clock::time_point(); + + // For some cases + if (is_stable > 0) + u.m_tsFreshActivation = steady_clock::time_point(); } else { HLOGC(gslog.Debug, log << "grp/sendBackup: link UNSTABLE for " << FormatDuration(currtime - u.m_tsUnstableSince) << " : @" << d->id << " - will send a payload"); + if (is_zero(u.m_tsUnstableSince)) + { + u.m_tsUnstableSince = currtime; + } } - return is_stable; + return is_stable >= 0; } // [[using locked(this->m_GroupLock)]] @@ -3810,12 +3857,10 @@ void CUDTGroup::sendBackup_SilenceRedundantLinks(vector& w_parallel) } CUDT& ce = d->ps->core(); steady_clock::duration td(0); - if (!is_zero(ce.m_tsFreshActivation) && - count_microseconds(td = currtime - ce.m_tsFreshActivation) < ce.m_uOPT_StabilityTimeout) + if (!is_zero(ce.m_tsFreshActivation) && sendBackup_CheckRunningLinkStable(ce, currtime, d->weight) != 1) { HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td) << " < " - << ce.m_uOPT_StabilityTimeout << "(stability timeout)"); + log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td)); continue; } From 2d14df227205bd327da6854430901d63aab5f4cd Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 8 Feb 2021 10:30:34 +0100 Subject: [PATCH 058/790] [core] Fixed bonding packet timestamping --- srtcore/group.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 15e7e7ace..802a4ad01 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1231,6 +1231,8 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } vector sendstates; + if (w_mc.srctime == 0) + w_mc.srctime = count_microseconds(steady_clock::now().time_since_epoch()); for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) { @@ -3960,6 +3962,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // Maximum weight of active links. uint16_t maxActiveWeight = 0; + if (w_mc.srctime == 0) + w_mc.srctime = count_microseconds(steady_clock::now().time_since_epoch()); // We believe that we need to send the payload over every activeLinks link anyway. for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) From 4744283aed293a2bc631e20be810c68e0e37110d Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Sun, 7 Feb 2021 17:28:36 +0800 Subject: [PATCH 059/790] [core] Fix warning of undefined ENABLE_EXPERIMENTAL_BONDING The warning can be triggered when srt.h is included by other project. --- srtcore/srt.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/srt.h b/srtcore/srt.h index 5317720db..30be3e061 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -161,6 +161,10 @@ static const int32_t SRTGROUP_MASK = (1 << 30); typedef int SYSSOCKET; #endif +#ifndef ENABLE_EXPERIMENTAL_BONDING +#define ENABLE_EXPERIMENTAL_BONDING 0 +#endif + typedef SYSSOCKET UDPSOCKET; From 28729391e767386e307da97cf00167271ea28bf5 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Sun, 7 Feb 2021 19:10:07 +0800 Subject: [PATCH 060/790] [core] Workaround ABI compatibility due to ENABLE_EXPERIMENTAL_BONDING --- srtcore/srt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/srt.h b/srtcore/srt.h index 30be3e061..639fbc1a3 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -239,12 +239,12 @@ typedef enum SRT_SOCKOPT { SRTO_ENFORCEDENCRYPTION, // Connection to be rejected or quickly broken when one side encryption set or bad password SRTO_IPV6ONLY, // IPV6_V6ONLY mode SRTO_PEERIDLETIMEO, // Peer-idle timeout (max time of silence heard from peer) in [ms] + SRTO_BINDTODEVICE, // Forward the SOL_SOCKET/SO_BINDTODEVICE option on socket (pass packets only from that device) #if ENABLE_EXPERIMENTAL_BONDING SRTO_GROUPCONNECT, // Set on a listener to allow group connection SRTO_GROUPSTABTIMEO, // Stability timeout (backup groups) in [us] SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake #endif - SRTO_BINDTODEVICE, // Forward the SOL_SOCKET/SO_BINDTODEVICE option on socket (pass packets only from that device) SRTO_PACKETFILTER = 60, // Add and configure a packet filter SRTO_RETRANSMITALGO = 61 // An option to select packet retransmission algorithm } SRT_SOCKOPT; From 28f20216656a082bb46c8ac3a309f2ba312ffc61 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 8 Feb 2021 17:15:42 +0100 Subject: [PATCH 061/790] [core] Minor fixes: warn, formatting, trace, etc. --- srtcore/core.cpp | 5 +---- srtcore/group.cpp | 4 ++-- srtcore/window.cpp | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 85bd2f369..73e370ed7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8776,10 +8776,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement { int32_t ack = 0; - int rtt = -1; - - // update RTT - rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack); + const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack); if (rtt <= 0) { LOGC(inlog.Error, diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 802a4ad01..1a28b03a0 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3027,7 +3027,7 @@ class StabilityTracer return; std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); - str_tnow.resize(str_tnow.size() - 6); // remove trailing ' [SYS]' part + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part while (str_tnow.find(':') != std::string::npos) { str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); } @@ -3051,7 +3051,7 @@ StabilityTracer s_stab_trace; /// @retval 1 - link is identified as stable /// @retval 0 - link state remains unchanged (too early to identify, still in activation phase) /// @retval -1 - link is identified as unstable -static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint16_t weight) +static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint16_t weight ATR_UNUSED) { const uint32_t latency_us = u.peerLatency_us(); const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. diff --git a/srtcore/window.cpp b/srtcore/window.cpp index 6e03b5d0b..0c40af128 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -85,7 +85,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 for (int i = r_iTail, n = r_iHead; i < n; ++ i) { - // looking for indentical ACK Seq. No. + // looking for identical ACK Seq. No. if (seq == r_aSeq[i].iACKSeqNo) { // return the Data ACK it carried From 94077c37592d4684cc28cecafab15d34f9c054c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 9 Feb 2021 16:36:15 +0100 Subject: [PATCH 062/790] [core] Fixed pre-initialization of the last sample time for groups --- srtcore/api.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4acf933a6..8b41eaf5b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1147,6 +1147,10 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ { u = s->m_GroupOf->m_GroupID; s->core().m_OPT_GroupConnect = 1; // should be derived from ls, but make sure + + // Mark the beginning of the connection at the moment + // when the group ID is returned to the app caller + s->m_GroupOf->m_stats.tsLastSampleTime = steady_clock::now(); } else { @@ -1616,6 +1620,8 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar // should block always if the group doesn't have neither 1 conencted link g.m_bOpened = true; + g.m_stats.tsLastSampleTime = steady_clock::now(); + f->laststatus = st; // Check the socket status and update it. // Turn the group state of the socket to IDLE only if From 40943db9134a6da418651c2c4b9427d5e7dec721 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Feb 2021 15:23:46 +0100 Subject: [PATCH 063/790] [core] Fixed FormatTime subseconds --- srtcore/group.cpp | 3 +-- srtcore/sync.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 1a28b03a0..4a6f14213 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3858,11 +3858,10 @@ void CUDTGroup::sendBackup_SilenceRedundantLinks(vector& w_parallel) continue; } CUDT& ce = d->ps->core(); - steady_clock::duration td(0); if (!is_zero(ce.m_tsFreshActivation) && sendBackup_CheckRunningLinkStable(ce, currtime, d->weight) != 1) { HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early: " << FormatDuration(td)); + log << "... not silencing @" << d->id << ": too early"); continue; } diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 45d568212..7fb494256 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -49,7 +49,7 @@ std::string FormatTime(const steady_clock::time_point& timestamp) out << setfill('0') << setw(2) << hours << ":" << setfill('0') << setw(2) << minutes << ":" << setfill('0') << setw(2) << seconds << "." - << setfill('0') << setw(decimals) << timestamp.time_since_epoch().count() << " [STDY]"; + << setfill('0') << setw(decimals) << (timestamp - seconds_from(total_sec)).time_since_epoch().count() << " [STDY]"; return out.str(); } From f1b35cbf5b9b42b031e9b119e4c802b5f744468c Mon Sep 17 00:00:00 2001 From: Christophe Giboudeaux Date: Wed, 10 Feb 2021 12:42:45 +0000 Subject: [PATCH 064/790] [core] Fix build with GCC 11. (#1806) The 'limits' header must be included explicitly. --- srtcore/sync.h | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/sync.h b/srtcore/sync.h index 59c125a75..9f01ca91e 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -15,6 +15,7 @@ //#define ENABLE_CXX17 #include +#include #ifdef ENABLE_STDCXX_SYNC #include #include From 66cb7c79ad499b82d071fcc0e2858a708dda0d28 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 10 Feb 2021 14:54:20 +0100 Subject: [PATCH 065/790] [core] Using numeric limits not only with C++11 (#1807) --- srtcore/sync.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/srtcore/sync.h b/srtcore/sync.h index 9f01ca91e..aeb4e0663 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -179,21 +179,8 @@ class TimePoint inline void operator-=(const Duration& rhs) { m_timestamp -= rhs.count(); } public: // -#if HAVE_FULL_CXX11 static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(numeric_limits::min()); } static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(numeric_limits::max()); } -#else -#ifndef UINT64_MAX -#define UNDEF_UINT64_MAX -#define UINT64_MAX 0xffffffffffffffffULL -#endif - static inline TimePoint min() { return TimePoint(0); } - static inline TimePoint max() { return TimePoint(UINT64_MAX); } - -#ifdef UNDEF_UINT64_MAX -#undef UINT64_MAX -#endif -#endif public: Duration time_since_epoch() const; From bdb3191ebd87f0d30bccd50ff65d634d9c1c39a7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 11 Feb 2021 14:36:53 +0100 Subject: [PATCH 066/790] [core] Fixed group rcv buffering of ignored packet (#1804) --- srtcore/group.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 4a6f14213..c4fe2e488 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2372,21 +2372,22 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // by "more correct data" if found more appropriate later. But we have to // copy these data anyway anywhere, even if they need to fall on the floor later. int stat; + char extrabuf[SRT_LIVE_MAX_PLSIZE]; + char* msgbuf = NULL; if (output_size) { - // We have already the data, so this must fall on the floor - char lostbuf[SRT_LIVE_MAX_PLSIZE]; - stat = ps->core().receiveMessage((lostbuf), SRT_LIVE_MAX_PLSIZE, (mctrl), CUDTUnited::ERH_RETURN); + // We already have the target data in `buf`. Now reading extra data potentially redundant (to be ignored) + // or AHEAD (to be buffered internally by the group) + msgbuf = extrabuf; + stat = ps->core().receiveMessage((extrabuf), SRT_LIVE_MAX_PLSIZE, (mctrl), CUDTUnited::ERH_RETURN); HLOGC(grlog.Debug, - log << "group/recv: @" << id << " IGNORED data with %" << mctrl.pktseq << " #" << mctrl.msgno - << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(lostbuf, stat))); - if (stat > 0) - { - m_stats.recvDiscard.Update(stat); - } + log << "group/recv: @" << id << " EXTRACTED EXTRA data with %" << mctrl.pktseq + << " #" << mctrl.msgno << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(extrabuf, stat)) + << (CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo) > 1 ? " - TO STORE" : " - TO IGNORE")); } else { + msgbuf = buf; stat = ps->core().receiveMessage((buf), len, (mctrl), CUDTUnited::ERH_RETURN); HLOGC(grlog.Debug, log << "group/recv: @" << id << " EXTRACTED data with %" << mctrl.pktseq << " #" @@ -2451,7 +2452,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " BEHIND base=%" << m_RcvBaseSeqNo << " - discarding"); // The sequence is recorded, the packet has to be discarded. - // That's all. + m_stats.recvDiscard.Update(stat); continue; } @@ -2464,7 +2465,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) HLOGC(grlog.Debug, log << "@" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " AHEAD base=%" << m_RcvBaseSeqNo); - p->packet.assign(buf, buf + stat); + p->packet.assign(msgbuf, msgbuf + stat); p->mctrl = mctrl; break; // Don't read from that socket anymore. } @@ -2713,8 +2714,8 @@ CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead() { // The very next packet. Return it. HLOGC(grlog.Debug, - log << "group/recv: Base %" << m_RcvBaseSeqNo << " ahead delivery POSSIBLE %" << a.mctrl.pktseq << "#" - << a.mctrl.msgno << " from @" << i->first << ")"); + log << "group/recv: Base %" << m_RcvBaseSeqNo << " ahead delivery POSSIBLE %" << a.mctrl.pktseq + << " #" << a.mctrl.msgno << " from @" << i->first << ")"); out = &a; } else if (seqdiff < 1 && !a.packet.empty()) From 375456203e3b3b965c9a69960f3e7cf9baebcfb0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 11 Feb 2021 15:29:39 +0100 Subject: [PATCH 067/790] [core] Created internal config storage for a socket (#1776) --- srtcore/api.cpp | 64 +- srtcore/channel.cpp | 100 +-- srtcore/channel.h | 41 +- srtcore/congctl.h | 19 +- srtcore/core.cpp | 1458 +++++++++------------------------- srtcore/core.h | 179 +---- srtcore/crypto.cpp | 4 +- srtcore/filelist.maf | 2 + srtcore/group.cpp | 59 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/queue.h | 11 +- srtcore/socketconfig.cpp | 197 +++++ srtcore/socketconfig.h | 1137 ++++++++++++++++++++++++++ srtcore/srt.h | 4 +- test/test_fec_rebuilding.cpp | 2 +- 16 files changed, 1878 insertions(+), 1405 deletions(-) create mode 100644 srtcore/socketconfig.cpp create mode 100644 srtcore/socketconfig.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 8b41eaf5b..5689cd1f4 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -168,7 +168,7 @@ bool CUDTSocket::readReady() bool CUDTSocket::writeReady() { return (m_pUDT->m_bConnected - && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_iSndBufSize)) + && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.m_iSndBufSize)) || broken(); } @@ -526,8 +526,8 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, << w_hs.m_iID << " - ADAPTING."); w_hs.m_iISN = ns->m_pUDT->m_iISN; - w_hs.m_iMSS = ns->m_pUDT->m_iMSS; - w_hs.m_iFlightFlagSize = ns->m_pUDT->m_iFlightFlagSize; + w_hs.m_iMSS = ns->m_pUDT->MSS(); + w_hs.m_iFlightFlagSize = ns->m_pUDT->m_config.m_iFlightFlagSize; w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; @@ -1000,7 +1000,7 @@ int CUDTUnited::listen(const SRTSOCKET u, int backlog) // [[using assert(s->m_Status == OPENED)]]; // listen is not supported in rendezvous connection setup - if (s->m_pUDT->m_bRendezvous) + if (s->m_pUDT->m_config.m_bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); s->m_uiBackLog = backlog; @@ -1073,7 +1073,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); // no "accept" in rendezvous connection setup - if (ls->m_pUDT->m_bRendezvous) + if (ls->m_pUDT->m_config.m_bRendezvous) { LOGC(cnlog.Fatal, log << "CUDTUnited::accept: RENDEZVOUS flag passed through check in srt_listen when it set listen state"); // This problem should never happen because `srt_listen` function should have @@ -1103,7 +1103,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ ls->m_QueuedSockets.erase(b); accepted = true; } - else if (!ls->m_pUDT->m_bSynRecving) + else if (!ls->m_pUDT->m_config.m_bSynRecving) { accepted = true; } @@ -1118,7 +1118,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ if (u == CUDT::INVALID_SOCK) { // non-blocking receiving, no connection available - if (!ls->m_pUDT->m_bSynRecving) + if (!ls->m_pUDT->m_config.m_bSynRecving) throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); // listening socket is closed @@ -1130,13 +1130,13 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ throw CUDTException(MJ_SETUP, MN_CLOSED, 0); // Set properly the SRTO_GROUPCONNECT flag - s->core().m_OPT_GroupConnect = 0; + s->core().m_config.m_GroupConnect = 0; // Check if LISTENER has the SRTO_GROUPCONNECT flag set, // and the already accepted socket has successfully joined // the mirror group. If so, RETURN THE GROUP ID, not the socket ID. #if ENABLE_EXPERIMENTAL_BONDING - if (ls->m_pUDT->m_OPT_GroupConnect == 1 && s->m_GroupOf) + if (ls->m_pUDT->m_config.m_GroupConnect == 1 && s->m_GroupOf) { // Put a lock to protect the group against accidental deletion // in the meantime. @@ -1146,7 +1146,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ if (s->m_GroupOf) { u = s->m_GroupOf->m_GroupID; - s->core().m_OPT_GroupConnect = 1; // should be derived from ls, but make sure + s->core().m_config.m_GroupConnect = 1; // should be derived from ls, but make sure // Mark the beginning of the connection at the moment // when the group ID is returned to the app caller @@ -1494,14 +1494,14 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar } // Set it the groupconnect option, as all in-group sockets should have. - ns->m_pUDT->m_OPT_GroupConnect = 1; + ns->m_pUDT->m_config.m_GroupConnect = 1; // Every group member will have always nonblocking // (this implies also non-blocking connect/accept). // The group facility functions will block when necessary // using epoll_wait. - ns->m_pUDT->m_bSynRecving = false; - ns->m_pUDT->m_bSynSending = false; + ns->m_pUDT->m_config.m_bSynRecving = false; + ns->m_pUDT->m_config.m_bSynSending = false; HLOGC(aclog.Debug, log << "groupConnect: NOTIFIED AS PENDING @" << sid << " both read and write"); // If this socket is not to block the current connect process, @@ -1823,7 +1823,7 @@ int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_ if (s->m_Status == SRTS_INIT) { - if (s->m_pUDT->m_bRendezvous) + if (s->m_pUDT->m_config.m_bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); // If bind() was done first on this socket, then the @@ -1953,7 +1953,7 @@ int CUDTUnited::close(CUDTSocket* s) HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); - bool synch_close_snd = s->m_pUDT->m_bSynSending; + const bool synch_close_snd = s->m_pUDT->m_config.m_bSynSending; SRTSOCKET u = s->m_SocketID; @@ -2318,7 +2318,7 @@ int CUDTUnited::selectEx( { if (s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() - < s->m_pUDT->m_iSndBufSize)) + < s->m_pUDT->m_config.m_iSndBufSize)) { writefds->push_back(s->m_SocketID); ++ count; @@ -2823,7 +2823,7 @@ void CUDTUnited::updateMux( // In such a case rely exclusively on that very socket and // use it the way as it is configured, of course, create also // always a new multiplexer for that very socket. - if (!udpsock && s->m_pUDT->m_bReuseAddr) + if (!udpsock && s->m_pUDT->m_config.m_bReuseAddr) { const int port = addr.hport(); @@ -2835,14 +2835,7 @@ void CUDTUnited::updateMux( // need to find an existing multiplexer that binds to the // given port in the same family as requested address. if ((i->second.m_iIPversion == addr.family()) - && (i->second.m_iMSS == s->m_pUDT->m_iMSS) - && (i->second.m_iIpTTL == s->m_pUDT->m_iIpTTL) - && (i->second.m_iIpToS == s->m_pUDT->m_iIpToS) -#ifdef SRT_ENABLE_BINDTODEVICE - && (i->second.m_BindToDevice == s->m_pUDT->m_BindToDevice) -#endif - && (i->second.m_iIpV6Only == s->m_pUDT->m_iIpV6Only) - && i->second.m_bReusable) + && i->second.m_mcfg == s->m_pUDT->m_config) { if (i->second.m_iPort == port) { @@ -2862,30 +2855,15 @@ void CUDTUnited::updateMux( // a new multiplexer is needed CMultiplexer m; - m.m_iMSS = s->m_pUDT->m_iMSS; + m.m_mcfg = s->m_pUDT->m_config; m.m_iIPversion = addr.family(); - m.m_iIpTTL = s->m_pUDT->m_iIpTTL; - m.m_iIpToS = s->m_pUDT->m_iIpToS; -#ifdef SRT_ENABLE_BINDTODEVICE - m.m_BindToDevice = s->m_pUDT->m_BindToDevice; -#endif m.m_iRefCount = 1; - m.m_iIpV6Only = s->m_pUDT->m_iIpV6Only; - m.m_bReusable = s->m_pUDT->m_bReuseAddr; m.m_iID = s->m_SocketID; try { m.m_pChannel = new CChannel(); - m.m_pChannel->setIpTTL(s->m_pUDT->m_iIpTTL); - m.m_pChannel->setIpToS(s->m_pUDT->m_iIpToS); -#ifdef SRT_ENABLE_BINDTODEVICE - m.m_pChannel->setBind(m.m_BindToDevice); -#endif - m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize); - m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize); - if (s->m_pUDT->m_iIpV6Only != -1) - m.m_pChannel->setIpV6Only(s->m_pUDT->m_iIpV6Only); + m.m_pChannel->setConfig(m.m_mcfg); if (udpsock) { @@ -3011,7 +2989,7 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) if (!mux && fallback) { // It is allowed to reuse this multiplexer, but the socket must allow both IPv4 and IPv6 - if (fallback->m_iIpV6Only == 0) + if (fallback->m_mcfg.m_iIpV6Only == 0) { HLOGC(smlog.Warn, log << "updateListenerMux: reusing multiplexer from different family"); mux = fallback; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 91aea4eb9..c6409ead8 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -137,13 +137,8 @@ static int set_cloexec(int fd, int set) { #endif // ifndef _WIN32 #endif // if ENABLE_CLOEXEC -CChannel::CChannel(): -m_iSocket(INVALID_SOCKET), -m_iIpTTL(-1), /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ -m_iIpToS(-1), /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ -m_iSndBufSize(65536), -m_iRcvBufSize(65536), -m_iIpV6Only(-1) +CChannel::CChannel() + :m_iSocket(INVALID_SOCKET) { } @@ -186,15 +181,16 @@ void CChannel::createSocket(int family) #endif #endif // ENABLE_SOCK_CLOEXEC - if ((m_iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) + if ((m_mcfg.m_iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)(&m_iIpV6Only), sizeof(m_iIpV6Only)); + int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, + m_mcfg.pvIpV6Only(), sizeof(int)); if (res == -1) { int err = errno; char msg[160]; - LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " << m_iIpV6Only - << ": " << SysStrError(err, msg, 159)); + LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " + << m_mcfg.m_iIpV6Only << ": " << SysStrError(err, msg, 159)); } } @@ -270,22 +266,22 @@ void CChannel::setUDPSockOpt() #if defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value int maxsize = 64000; - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), sizeof(int))) ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))) + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), sizeof(int))) ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); #else // for other systems, if requested is greated than maximum, the maximum value will be automactally used - if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) || - (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))) + if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), sizeof(int))) || + (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), sizeof(int)))) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #endif - if (-1 != m_iIpTTL) + if (m_mcfg.m_iIpTTL != -1) { if (m_BindAddr.family() == AF_INET) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), sizeof (int))) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } else @@ -295,7 +291,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, m_mcfg.pvIpTTL(), sizeof (int))) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -303,7 +299,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), sizeof (int))) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -311,11 +307,11 @@ void CChannel::setUDPSockOpt() } } - if (-1 != m_iIpToS) + if (m_mcfg.m_iIpToS != -1) { if (m_BindAddr.family() == AF_INET) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), sizeof (int))) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } else @@ -326,7 +322,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, m_mcfg.pvIpToS(), sizeof (int))) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -336,7 +332,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), sizeof (int))) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -345,7 +341,7 @@ void CChannel::setUDPSockOpt() } #ifdef SRT_ENABLE_BINDTODEVICE - if (!m_BindToDevice.empty()) + if (!m_mcfg.m_BindToDevice.empty()) { if (m_BindAddr.family() != AF_INET) { @@ -353,7 +349,8 @@ void CChannel::setUDPSockOpt() throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_BindToDevice.c_str(), m_BindToDevice.size())) + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, + m_mcfg.m_BindToDevice.c_str(), m_mcfg.m_BindToDevice.size())) { char buf[255]; const char* err = SysStrError(NET_ERROR, buf, 255); @@ -401,30 +398,20 @@ void CChannel::close() const int CChannel::getSndBufSize() { socklen_t size = sizeof(socklen_t); - ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size); - return m_iSndBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), &size); + return m_mcfg.m_iUDPSndBufSize; } int CChannel::getRcvBufSize() { socklen_t size = sizeof(socklen_t); - ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size); - return m_iRcvBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), &size); + return m_mcfg.m_iUDPRcvBufSize; } -void CChannel::setSndBufSize(int size) +void CChannel::setConfig(const CSrtMuxerConfig& config) { - m_iSndBufSize = size; -} - -void CChannel::setRcvBufSize(int size) -{ - m_iRcvBufSize = size; -} - -void CChannel::setIpV6Only(int ipV6Only) -{ - m_iIpV6Only = ipV6Only; + m_mcfg = config; } int CChannel::getIpTTL() const @@ -432,14 +419,14 @@ int CChannel::getIpTTL() const if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - socklen_t size = sizeof(m_iIpTTL); + socklen_t size = sizeof(int); if (m_BindAddr.family() == AF_INET) { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char *)&m_iIpTTL, &size); + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), &size); } else if (m_BindAddr.family() == AF_INET6) { - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&m_iIpTTL, &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, m_mcfg.pvIpTTL(), &size); } else { @@ -447,7 +434,7 @@ int CChannel::getIpTTL() const LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - return m_iIpTTL; + return m_mcfg.m_iIpTTL; } int CChannel::getIpToS() const @@ -455,15 +442,15 @@ int CChannel::getIpToS() const if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - socklen_t size = sizeof(m_iIpToS); + socklen_t size = sizeof(int); if (m_BindAddr.family() == AF_INET) { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char *)&m_iIpToS, &size); + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), &size); } else if (m_BindAddr.family() == AF_INET6) { #ifdef IPV6_TCLASS - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char *)&m_iIpToS, &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, m_mcfg.pvIpToS(), &size); #endif } else @@ -472,25 +459,10 @@ int CChannel::getIpToS() const LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - return m_iIpToS; -} - -void CChannel::setIpTTL(int ttl) -{ - m_iIpTTL = ttl; -} - -void CChannel::setIpToS(int tos) -{ - m_iIpToS = tos; + return m_mcfg.m_iIpToS; } #ifdef SRT_ENABLE_BINDTODEVICE -void CChannel::setBind(const string& name) -{ - m_BindToDevice = name; -} - bool CChannel::getBind(char* dst, size_t len) { if (m_iSocket == INVALID_SOCKET) diff --git a/srtcore/channel.h b/srtcore/channel.h index 52508e47f..6ec51c875 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -56,6 +56,7 @@ modified by #include "platform_sys.h" #include "udt.h" #include "packet.h" +#include "socketconfig.h" #include "netinet_any.h" class CChannel @@ -98,21 +99,6 @@ class CChannel int getRcvBufSize(); - /// Set the UDP sending buffer size. - /// @param [in] size expected UDP sending buffer size. - - void setSndBufSize(int size); - - /// Set the UDP receiving buffer size. - /// @param [in] size expected UDP receiving buffer size. - - void setRcvBufSize(int size); - - /// Set the IPV6ONLY option. - /// @param [in] IPV6ONLY value. - - void setIpV6Only(int ipV6Only); - /// Query the socket address that the channel is using. /// @param [out] addr pointer to store the returned socket address. @@ -137,16 +123,7 @@ class CChannel EReadStatus recvfrom(sockaddr_any& addr, CPacket& packet) const; - /// Set the IP TTL. - /// @param [in] ttl IP Time To Live. - /// @return none. - - void setIpTTL(int ttl); - - /// Set the IP Type of Service. - /// @param [in] tos IP Type of Service. - - void setIpToS(int tos); + void setConfig(const CSrtMuxerConfig& config); /// Get the IP TTL. /// @param [in] ttl IP Time To Live. @@ -160,7 +137,6 @@ class CChannel int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE - void setBind(const std::string& name); bool getBind(char* dst, size_t len); #endif @@ -176,14 +152,11 @@ class CChannel private: UDPSOCKET m_iSocket; // socket descriptor - int m_iIpTTL; - int m_iIpToS; -#ifdef SRT_ENABLE_BINDTODEVICE - std::string m_BindToDevice; -#endif - int m_iSndBufSize; // UDP sending buffer size - int m_iRcvBufSize; // UDP receiving buffer size - int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) + + // Mutable because when querying original settings + // this comprises the cache for extracted values, + // although the object itself isn't considered modified. + mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. sockaddr_any m_BindAddr; }; diff --git a/srtcore/congctl.h b/srtcore/congctl.h index cc44f02c9..bf6826c28 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -11,6 +11,7 @@ #ifndef INC_SRT_CONGCTL_H #define INC_SRT_CONGCTL_H +#include #include #include #include @@ -55,13 +56,24 @@ class SrtCongestion bool operator()(NamePtr np) { return n == np.first; } }; + static NamePtr* find(const std::string& name) + { + NamePtr* end = congctls+N_CONTROLLERS; + NamePtr* try_selector = std::find_if(congctls, end, IsName(name)); + return try_selector != end ? try_selector : NULL; + } + + static bool exists(const std::string& name) + { + return find(name); + } + // You can call select() multiple times, until finally // the 'configure' method is called. bool select(const std::string& name) { - NamePtr* end = congctls+N_CONTROLLERS; - NamePtr* try_selector = std::find_if(congctls, end, IsName(name)); - if (try_selector == end) + NamePtr* try_selector = find(name); + if (!try_selector) return false; selector = try_selector - congctls; return true; @@ -117,6 +129,7 @@ class SrtCongestion }; }; +class CPacket; class SrtCongestionControlBase { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 73e370ed7..0c9540d6d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -87,16 +87,6 @@ CUDTUnited CUDT::s_UDTUnited; const SRTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; const int UDT::ERROR = CUDT::ERROR; -// SRT Version constants -#define SRT_VERSION_UNK 0 -#define SRT_VERSION_MAJ1 0x010000 /* Version 1 major */ -#define SRT_VERSION_MAJ(v) (0xFF0000 & (v)) /* Major number ensuring backward compatibility */ -#define SRT_VERSION_MIN(v) (0x00FF00 & (v)) -#define SRT_VERSION_PCH(v) (0x0000FF & (v)) - -// NOTE: SRT_VERSION is primarily defined in the build file. -extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); - //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ #define SRT_CMD_HSREQ_SZ 12 /* Current version packet size */ @@ -131,7 +121,6 @@ void CUDT::construct() m_pSndLossList = NULL; m_pRcvLossList = NULL; m_iReorderTolerance = 0; - m_iMaxReorderTolerance = 0; // Sensible optimal value is 10, 0 preserves old behavior m_iConsecEarlyDelivery = 0; // how many times so far the packet considered lost has been received before TTL expires m_iConsecOrderedDelivery = 0; @@ -156,9 +145,7 @@ void CUDT::construct() m_tsLastReqTime = steady_clock::time_point(); m_SrtHsSide = HSD_DRAW; - m_lSrtVersion = SRT_DEF_VERSION; m_lPeerSrtVersion = 0; // not defined until connected. - m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1; m_iTsbPdDelay_ms = 0; m_iPeerTsbPdDelay_ms = 0; @@ -170,9 +157,6 @@ void CUDT::construct() m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; - m_uKmRefreshRatePkt = 0; - m_uKmPreAnnouncePkt = 0; - // Initilize mutex and condition variables initSynch(); @@ -186,58 +170,13 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent) (void)SRT_DEF_VERSION; - // Default UDT configurations - m_iMSS = DEF_MSS; - m_bSynSending = true; - m_bSynRecving = true; - m_iFlightFlagSize = DEF_FLIGHT_SIZE; - m_iSndBufSize = DEF_BUFFER_SIZE; - m_iRcvBufSize = DEF_BUFFER_SIZE; - m_iUDPSndBufSize = DEF_UDP_BUFFER_SIZE; - m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; - - // Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option - // for other modes. - m_Linger.l_onoff = 0; - m_Linger.l_linger = 0; - m_bRendezvous = false; - m_tdConnTimeOut = seconds_from(DEF_CONNTIMEO_S); - m_bDriftTracer = true; - m_iSndTimeOut = -1; - m_iRcvTimeOut = -1; - m_bReuseAddr = true; - m_llMaxBW = -1; - m_iIpTTL = -1; - m_iIpToS = -1; - m_CryptoSecret.len = 0; - m_iSndCryptoKeyLen = 0; - // Cfg - m_bDataSender = false; // Sender only if true: does not recv data - m_bOPT_TsbPd = true; // Enable TsbPd on sender - m_iOPT_TsbPdDelay = SRT_LIVE_DEF_LATENCY_MS; - m_iOPT_PeerTsbPdDelay = 0; // Peer's TsbPd delay as receiver (here is its minimum value, if used) - m_bOPT_TLPktDrop = true; - m_iOPT_SndDropDelay = 0; - m_bOPT_StrictEncryption = true; - m_iOPT_PeerIdleTimeout = COMM_RESPONSE_TIMEOUT_MS; - m_uOPT_StabilityTimeout = CUDT::COMM_DEF_STABILITY_TIMEOUT_US; - m_OPT_GroupConnect = 0; + // Runtime fields #if ENABLE_EXPERIMENTAL_BONDING m_HSGroupType = SRT_GTYPE_UNDEFINED; #endif - m_iOPT_RetransmitAlgo = 0; m_bTLPktDrop = true; // Too-late Packet Drop - m_bMessageAPI = true; - m_zOPT_ExpPayloadSize = SRT_LIVE_DEF_PLSIZE; - m_iIpV6Only = -1; - // Runtime - m_bRcvNakReport = true; // Receiver's Periodic NAK Reports - m_llInputBW = 0; // Application provided input bandwidth (0: internal input rate sampling) - m_iOverheadBW = 25; // Percent above input stream rate (applies if m_llMaxBW == 0) - m_OPT_PktFilterConfigString = ""; m_pCache = NULL; - // This is in order to set it ANY kind of initial value, however // this value should not be used when not connected and should be // updated in the handshake. When this value is 0, it means that @@ -246,14 +185,6 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent) // overridden before any sending happens. m_iFlowWindowSize = 0; - // Default congctl is "live". - // Available builtin congctl: "file". - // Other congctls can be registerred. - - // Note that 'select' returns false if there's no such congctl. - // If so, congctl becomes unselected. Calling 'configure' on an - // unselected congctl results in exception. - m_CongCtl.select("live"); } CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) @@ -263,64 +194,13 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) // XXX Consider all below fields (except m_bReuseAddr) to be put // into a separate class for easier copying. - // Default UDT configurations - m_iMSS = ancestor.m_iMSS; - m_bSynSending = ancestor.m_bSynSending; - m_bSynRecving = ancestor.m_bSynRecving; - m_iFlightFlagSize = ancestor.m_iFlightFlagSize; - m_iSndBufSize = ancestor.m_iSndBufSize; - m_iRcvBufSize = ancestor.m_iRcvBufSize; - m_Linger = ancestor.m_Linger; - m_bDriftTracer = ancestor.m_bDriftTracer; - m_iUDPSndBufSize = ancestor.m_iUDPSndBufSize; - m_iUDPRcvBufSize = ancestor.m_iUDPRcvBufSize; - m_bRendezvous = ancestor.m_bRendezvous; - m_SrtHsSide = ancestor.m_SrtHsSide; // actually it sets it to HSD_RESPONDER - m_tdConnTimeOut = ancestor.m_tdConnTimeOut; - m_iSndTimeOut = ancestor.m_iSndTimeOut; - m_iRcvTimeOut = ancestor.m_iRcvTimeOut; - m_bReuseAddr = true; // this must be true, because all accepted sockets share the same port with the listener - m_llMaxBW = ancestor.m_llMaxBW; - m_iIpTTL = ancestor.m_iIpTTL; - m_iIpToS = ancestor.m_iIpToS; - m_llInputBW = ancestor.m_llInputBW; - m_iOverheadBW = ancestor.m_iOverheadBW; - m_bDataSender = ancestor.m_bDataSender; - m_bOPT_TsbPd = ancestor.m_bOPT_TsbPd; - m_iOPT_TsbPdDelay = ancestor.m_iOPT_TsbPdDelay; - m_iOPT_PeerTsbPdDelay = ancestor.m_iOPT_PeerTsbPdDelay; - m_bOPT_TLPktDrop = ancestor.m_bOPT_TLPktDrop; - m_iOPT_SndDropDelay = ancestor.m_iOPT_SndDropDelay; - m_bOPT_StrictEncryption = ancestor.m_bOPT_StrictEncryption; - m_iOPT_RetransmitAlgo = ancestor.m_iOPT_RetransmitAlgo; - m_iOPT_PeerIdleTimeout = ancestor.m_iOPT_PeerIdleTimeout; - m_uOPT_StabilityTimeout = ancestor.m_uOPT_StabilityTimeout; - m_OPT_GroupConnect = ancestor.m_OPT_GroupConnect; // NOTE: on single accept set back to 0 - m_zOPT_ExpPayloadSize = ancestor.m_zOPT_ExpPayloadSize; - m_bTLPktDrop = ancestor.m_bTLPktDrop; - m_bMessageAPI = ancestor.m_bMessageAPI; - m_iIpV6Only = ancestor.m_iIpV6Only; - m_iReorderTolerance = ancestor.m_iMaxReorderTolerance; // Initialize with maximum value - m_iMaxReorderTolerance = ancestor.m_iMaxReorderTolerance; - // Runtime - m_bRcvNakReport = ancestor.m_bRcvNakReport; - m_OPT_PktFilterConfigString = ancestor.m_OPT_PktFilterConfigString; - - m_CryptoSecret = ancestor.m_CryptoSecret; - m_iSndCryptoKeyLen = ancestor.m_iSndCryptoKeyLen; - - m_uKmRefreshRatePkt = ancestor.m_uKmRefreshRatePkt; - m_uKmPreAnnouncePkt = ancestor.m_uKmPreAnnouncePkt; + m_config = ancestor.m_config; + m_SrtHsSide = ancestor.m_SrtHsSide; // actually it sets it to HSD_RESPONDER + m_bTLPktDrop = ancestor.m_bTLPktDrop; + m_iReorderTolerance = m_config.m_iMaxReorderTolerance; // Initialize with maximum value + // Runtime m_pCache = ancestor.m_pCache; - - // SrtCongestion's copy constructor copies the selection, - // but not the underlying congctl object. After - // copy-constructed, the 'configure' must be called on it again. - m_CongCtl = ancestor.m_CongCtl; - - m_lSrtVersion = ancestor.m_lSrtVersion; - m_lMinimumPeerSrtVersion = ancestor.m_lMinimumPeerSrtVersion; } CUDT::~CUDT() @@ -328,9 +208,6 @@ CUDT::~CUDT() // release mutex/condtion variables destroySynch(); - // Wipeout critical data - memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); - // destroy the data structures delete m_pSndBuffer; delete m_pRcvBuffer; @@ -355,11 +232,81 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_LOSSMAXTTL }; +static const int32_t + SRTO_R_PREBIND = BIT(0), + SRTO_R_PRE = BIT(1), + SRTO_POST_SPEC = BIT(2); + +struct SrtOptionAction +{ + int flags[SRTO_E_SIZE]; + SrtOptionAction() + { + // Set everything to 0 to clear all flags + // When an option isn't present here, it means that: + // * it is not settable, or + // * the option is POST (non-restricted) + // * it has no post-actions + // The post-action may be defined independently on restrictions. + memset(flags, 0, sizeof flags); + + flags[SRTO_MSS] = SRTO_R_PREBIND; + flags[SRTO_FC] = SRTO_R_PRE; + flags[SRTO_SNDBUF] = SRTO_R_PREBIND; + flags[SRTO_RCVBUF] = SRTO_R_PREBIND; + flags[SRTO_UDP_SNDBUF] = SRTO_R_PREBIND; + flags[SRTO_UDP_RCVBUF] = SRTO_R_PREBIND; + flags[SRTO_RENDEZVOUS] = SRTO_R_PRE; + flags[SRTO_REUSEADDR] = SRTO_R_PREBIND; + flags[SRTO_MAXBW] = SRTO_POST_SPEC; + flags[SRTO_SENDER] = SRTO_R_PRE; + flags[SRTO_TSBPDMODE] = SRTO_R_PRE; + flags[SRTO_LATENCY] = SRTO_R_PRE; + flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_PASSPHRASE] = SRTO_R_PRE; + flags[SRTO_PBKEYLEN] = SRTO_R_PRE; + flags[SRTO_IPTTL] = SRTO_R_PREBIND; + flags[SRTO_IPTOS] = SRTO_R_PREBIND; + flags[SRTO_TLPKTDROP] = SRTO_R_PRE; + flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; + flags[SRTO_NAKREPORT] = SRTO_R_PRE; + flags[SRTO_VERSION] = SRTO_R_PRE; + flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; + flags[SRTO_RCVLATENCY] = SRTO_R_PRE; + flags[SRTO_PEERLATENCY] = SRTO_R_PRE; + flags[SRTO_MINVERSION] = SRTO_R_PRE; + flags[SRTO_STREAMID] = SRTO_R_PRE; + flags[SRTO_CONGESTION] = SRTO_R_PRE; + flags[SRTO_MESSAGEAPI] = SRTO_R_PRE; + flags[SRTO_PAYLOADSIZE] = SRTO_R_PRE; + flags[SRTO_TRANSTYPE] = SRTO_R_PREBIND; + flags[SRTO_KMREFRESHRATE] = SRTO_R_PRE; + flags[SRTO_KMPREANNOUNCE] = SRTO_R_PRE; + flags[SRTO_ENFORCEDENCRYPTION] = SRTO_R_PRE; + flags[SRTO_IPV6ONLY] = SRTO_R_PREBIND; + flags[SRTO_PEERIDLETIMEO] = SRTO_R_PRE; +#if ENABLE_EXPERIMENTAL_BONDING + flags[SRTO_GROUPCONNECT] = SRTO_R_PRE; +#endif + flags[SRTO_PACKETFILTER] = SRTO_R_PRE; + flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE; + } +} +srt_options_action; + void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) { if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + // Match check (confirm optName as index for srt_options_action) + if (int(optName) < 0 || int(optName) >= int(SRTO_E_SIZE)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Restriction check + int oflags = srt_options_action.flags[optName]; + ScopedLock cg (m_ConnectionLock); ScopedLock sendguard (m_SendLock); ScopedLock recvguard (m_RecvLock); @@ -367,623 +314,48 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) HLOGC(aclog.Debug, log << CONID() << "OPTION: #" << optName << " value:" << FormatBinaryString((uint8_t*)optval, optlen)); - switch (optName) - { - case SRTO_MSS: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - if (cast_optval(optval, optlen) < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - m_iMSS = cast_optval(optval); - - // Packet size cannot be greater than UDP buffer size - if (m_iMSS > m_iUDPSndBufSize) - m_iMSS = m_iUDPSndBufSize; - if (m_iMSS > m_iUDPRcvBufSize) - m_iMSS = m_iUDPRcvBufSize; - - break; - - case SRTO_SNDSYN: - m_bSynSending = cast_optval(optval, optlen); - break; - - case SRTO_RCVSYN: - m_bSynRecving = cast_optval(optval, optlen); - break; - - case SRTO_FC: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - if (cast_optval(optval, optlen) < 1) - throw CUDTException(MJ_NOTSUP, MN_INVAL); - - // Mimimum recv flight flag size is 32 packets - if (cast_optval(optval) > 32) - m_iFlightFlagSize = *(int *)optval; - else - m_iFlightFlagSize = 32; - - break; - - case SRTO_SNDBUF: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - if (cast_optval(optval, optlen) <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - m_iSndBufSize = cast_optval(optval) / (m_iMSS - CPacket::UDP_HDR_SIZE); - - break; - - case SRTO_RCVBUF: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - { - const int val = cast_optval(optval, optlen); - if (val <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - // Mimimum recv buffer size is 32 packets - const int mssin_size = m_iMSS - CPacket::UDP_HDR_SIZE; - - // XXX This magic 32 deserves some constant - if (val > mssin_size * 32) - m_iRcvBufSize = val / mssin_size; - else - m_iRcvBufSize = 32; - - // recv buffer MUST not be greater than FC size - if (m_iRcvBufSize > m_iFlightFlagSize) - m_iRcvBufSize = m_iFlightFlagSize; - } + if (IsSet(oflags, SRTO_R_PREBIND) && m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - break; - - case SRTO_LINGER: - m_Linger = cast_optval(optval, optlen); - break; - - case SRTO_UDP_SNDBUF: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - m_iUDPSndBufSize = cast_optval(optval, optlen); - - if (m_iUDPSndBufSize < m_iMSS) - m_iUDPSndBufSize = m_iMSS; - - break; - - case SRTO_UDP_RCVBUF: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - m_iUDPRcvBufSize = cast_optval(optval, optlen); - - if (m_iUDPRcvBufSize < m_iMSS) - m_iUDPRcvBufSize = m_iMSS; - - break; - - case SRTO_RENDEZVOUS: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - m_bRendezvous = cast_optval(optval, optlen); - break; - - case SRTO_SNDTIMEO: - m_iSndTimeOut = cast_optval(optval, optlen); - break; - - case SRTO_RCVTIMEO: - m_iRcvTimeOut = cast_optval(optval, optlen); - break; - - case SRTO_REUSEADDR: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - m_bReuseAddr = cast_optval(optval, optlen); - break; - - case SRTO_MAXBW: - m_llMaxBW = cast_optval(optval, optlen); - - // This can be done on both connected and unconnected socket. - // When not connected, this will do nothing, however this - // event will be repeated just after connecting anyway. - if (m_bConnected) - updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET)); - break; - - case SRTO_IPTTL: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - if (!(cast_optval(optval, optlen) == -1) && !((cast_optval(optval) >= 1) && (cast_optval(optval) <= 255))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - m_iIpTTL = cast_optval(optval); - break; - - case SRTO_IPTOS: - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - m_iIpToS = cast_optval(optval, optlen); - break; - - case SRTO_BINDTODEVICE: -#ifdef SRT_ENABLE_BINDTODEVICE - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - - { - string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); - if (val.size() >= IFNAMSIZ) - { - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - m_BindToDevice = val; - } -#else - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - break; - - case SRTO_INPUTBW: - m_llInputBW = cast_optval(optval, optlen); - // (only if connected; if not, then the value - // from m_iOverheadBW will be used initially) - if (m_bConnected) - updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW)); - break; - - case SRTO_OHEADBW: - if ((cast_optval(optval, optlen) < 5) || (cast_optval(optval) > 100)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - m_iOverheadBW = cast_optval(optval); - - // Changed overhead BW, so spread the change - // (only if connected; if not, then the value - // from m_iOverheadBW will be used initially) - if (m_bConnected) - updateCC(TEV_INIT, EventVariant(TEV_INIT_OHEADBW)); - break; - - case SRTO_SENDER: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_bDataSender = cast_optval(optval, optlen); - break; - - case SRTO_TSBPDMODE: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_bOPT_TsbPd = cast_optval(optval, optlen); - break; - - case SRTO_LATENCY: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_iOPT_TsbPdDelay = cast_optval(optval, optlen); - m_iOPT_PeerTsbPdDelay = cast_optval(optval); - break; - - case SRTO_RCVLATENCY: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_iOPT_TsbPdDelay = cast_optval(optval, optlen); - break; - - case SRTO_PEERLATENCY: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_iOPT_PeerTsbPdDelay = cast_optval(optval, optlen); - break; - - case SRTO_TLPKTDROP: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_bOPT_TLPktDrop = cast_optval(optval, optlen); - break; - - case SRTO_SNDDROPDELAY: - // Surprise: you may be connected to alter this option. - // The application may manipulate this option on sender while transmitting. - m_iOPT_SndDropDelay = cast_optval(optval, optlen); - break; - - case SRTO_PASSPHRASE: - // For consistency, throw exception when connected, - // no matter if otherwise the password can be set. - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - -#ifdef SRT_ENABLE_ENCRYPTION - // Password must be 10-80 characters. - // Or it can be empty to clear the password. - if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); - m_CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; - m_CryptoSecret.len = (optlen <= (int)sizeof(m_CryptoSecret.str) ? optlen : (int)sizeof(m_CryptoSecret.str)); - memcpy((m_CryptoSecret.str), optval, m_CryptoSecret.len); -#else - if (optlen == 0) - break; - - LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - break; - - case SRTO_PBKEYLEN: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); -#ifdef SRT_ENABLE_ENCRYPTION - { - const int v = cast_optval(optval, optlen); - int allowed[4] = { - 0, // Default value, if this results for initiator, defaults to 16. See below. - 16, // AES-128 - 24, // AES-192 - 32 // AES-256 - }; - int *allowed_end = allowed + 4; - if (find(allowed, allowed_end, v) == allowed_end) - { - LOGC(aclog.Error, - log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - // Note: This works a little different in HSv4 and HSv5. - - // HSv4: - // The party that is set SRTO_SENDER will send KMREQ, and it will - // use default value 16, if SRTO_PBKEYLEN is the default value 0. - // The responder that receives KMRSP has nothing to say about - // PBKEYLEN anyway and it will take the length of the key from - // the initiator (sender) as a good deal. - // - // HSv5: - // The initiator (independently on the sender) will send KMREQ, - // and as it should be the sender to decide about the PBKEYLEN. - // Your application should do the following then: - // 1. The sender should set PBKEYLEN to the required value. - // 2. If the sender is initiator, it will create the key using - // its preset PBKEYLEN (or default 16, if not set) and the - // receiver-responder will take it as a good deal. - // 3. Leave the PBKEYLEN value on the receiver as default 0. - // 4. If sender is responder, it should then advertise the PBKEYLEN - // value in the initial handshake messages (URQ_INDUCTION if - // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case - // of rendezvous, as it is the matter of luck who of them will - // eventually become the initiator). This way the receiver - // being an initiator will set m_iSndCryptoKeyLen before setting - // up KMREQ for sending to the sender-responder. - // - // Note that in HSv5 if both sides set PBKEYLEN, the responder - // wins, unless the initiator is a sender (the effective PBKEYLEN - // will be the one advertised by the responder). If none sets, - // PBKEYLEN will default to 16. + if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting)) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_iSndCryptoKeyLen = v; - } -#else - LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); + // Option execution. If this returns -1, there's no such option. + int status = m_config.set(optName, optval, optlen); + if (status == -1) + { + LOGC(aclog.Error, log << CONID() << "OPTION: #" << optName << " UNKNOWN"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - break; - - case SRTO_NAKREPORT: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_bRcvNakReport = cast_optval(optval, optlen); - break; - - case SRTO_CONNTIMEO: - m_tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); - break; - - case SRTO_DRIFTTRACER: - m_bDriftTracer = cast_optval(optval, optlen); - break; - - case SRTO_LOSSMAXTTL: - m_iMaxReorderTolerance = cast_optval(optval, optlen); - if (!m_bConnected) - m_iReorderTolerance = m_iMaxReorderTolerance; - break; - - case SRTO_VERSION: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_lSrtVersion = cast_optval(optval, optlen); - break; - - case SRTO_MINVERSION: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_lMinimumPeerSrtVersion = cast_optval(optval, optlen); - break; - - case SRTO_STREAMID: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - if (size_t(optlen) > MAX_SID_LENGTH) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - m_sStreamName.assign((const char*)optval, optlen); - break; - - case SRTO_CONGESTION: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - { - string val; - if (optlen == -1) - val = (const char*)optval; - else - val.assign((const char*)optval, optlen); - - // Translate alias - if (val == "vod") - val = "file"; - - bool res = m_CongCtl.select(val); - if (!res) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - break; - - case SRTO_MESSAGEAPI: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_bMessageAPI = cast_optval(optval, optlen); - break; - - case SRTO_PAYLOADSIZE: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - if (*(int *)optval > SRT_LIVE_MAX_PLSIZE) - { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (m_OPT_PktFilterConfigString != "") - { - // This means that the filter might have been installed before, - // and the fix to the maximum payload size was already applied. - // This needs to be checked now. - SrtFilterConfig fc; - if (!ParseFilterConfig(m_OPT_PktFilterConfigString, fc)) - { - // Break silently. This should not happen - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (m_zOPT_ExpPayloadSize > efc_max_payload_size) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size - << " required for packet filter header"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - m_zOPT_ExpPayloadSize = cast_optval(optval, optlen); - break; - - case SRTO_TRANSTYPE: - if (m_bConnecting || m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - // XXX Note that here the configuration for SRTT_LIVE - // is the same as DEFAULT VALUES for these fields set - // in CUDT::CUDT. - switch (cast_optval(optval, optlen)) - { - case SRTT_LIVE: - // Default live options: - // - tsbpd: on - // - latency: 120ms - // - linger: off - // - congctl: live - // - extraction method: message (reading call extracts one message) - m_bOPT_TsbPd = true; - m_iOPT_TsbPdDelay = SRT_LIVE_DEF_LATENCY_MS; - m_iOPT_PeerTsbPdDelay = 0; - m_bOPT_TLPktDrop = true; - m_iOPT_SndDropDelay = 0; - m_bMessageAPI = true; - m_bRcvNakReport = true; - m_zOPT_ExpPayloadSize = SRT_LIVE_DEF_PLSIZE; - m_Linger.l_onoff = 0; - m_Linger.l_linger = 0; - m_CongCtl.select("live"); - break; - - case SRTT_FILE: - // File transfer mode: - // - tsbpd: off - // - latency: 0 - // - linger: 2 minutes (180s) - // - congctl: file (original UDT congestion control) - // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) - m_bOPT_TsbPd = false; - m_iOPT_TsbPdDelay = 0; - m_iOPT_PeerTsbPdDelay = 0; - m_bOPT_TLPktDrop = false; - m_iOPT_SndDropDelay = -1; - m_bMessageAPI = false; - m_bRcvNakReport = false; - m_zOPT_ExpPayloadSize = 0; // use maximum - m_Linger.l_onoff = 1; - m_Linger.l_linger = DEF_LINGER_S; - m_CongCtl.select("file"); - break; - - default: - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - break; - -#if ENABLE_EXPERIMENTAL_BONDING - case SRTO_GROUPCONNECT: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_OPT_GroupConnect = cast_optval(optval, optlen); - break; -#endif - - case SRTO_KMREFRESHRATE: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - // If you first change the KMREFRESHRATE, KMPREANNOUNCE - // will be set to the maximum allowed value - m_uKmRefreshRatePkt = cast_optval(optval, optlen); - if (m_uKmPreAnnouncePkt == 0 || m_uKmPreAnnouncePkt > (m_uKmRefreshRatePkt - 1) / 2) - { - m_uKmPreAnnouncePkt = (m_uKmRefreshRatePkt - 1) / 2; - LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << hex << m_uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" - << hex << m_uKmPreAnnouncePkt); - } - break; - - case SRTO_KMPREANNOUNCE: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - { - const int val = cast_optval(optval, optlen); - const int kmref = m_uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_uKmRefreshRatePkt; - if (val > (kmref - 1) / 2) - { - LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) - << " - OPTION REJECTED."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - m_uKmPreAnnouncePkt = val; - } - break; - - case SRTO_ENFORCEDENCRYPTION: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_bOPT_StrictEncryption = cast_optval(optval, optlen); - break; - - case SRTO_PEERIDLETIMEO: + } + // Post-action, if applicable + if (IsSet(oflags, SRTO_POST_SPEC)) + { if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - m_iOPT_PeerIdleTimeout = cast_optval(optval, optlen); - break; - - case SRTO_IPV6ONLY: if (m_bOpened) throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - m_iIpV6Only = cast_optval(optval, optlen); - break; - - case SRTO_PACKETFILTER: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - { - string arg((const char*)optval, optlen); - // Parse the configuration string prematurely - SrtFilterConfig fc; - if (!ParseFilterConfig(arg, fc)) + switch (optName) { - LOGC(aclog.Error, - log << "SRTO_FILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " - "FILTERTYPE (" - << fc.type << ") must be installed (or builtin)"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (m_zOPT_ExpPayloadSize > efc_max_payload_size) - { - LOGC(aclog.Warn, - log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " - << efc_max_payload_size << " bytes"); - m_zOPT_ExpPayloadSize = efc_max_payload_size; - } - - m_OPT_PktFilterConfigString = arg; - } - break; + case SRTO_MAXBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET)); + break; -#if ENABLE_EXPERIMENTAL_BONDING - case SRTO_GROUPSTABTIMEO: - { - // This option is meaningless for the socket itself. - // It's set here just for the sake of setting it on a listener - // socket so that it is then applied on the group when a - // group connection is configuired. - const int val = cast_optval(optval, optlen); + case SRTO_INPUTBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW)); + break; - // Search if you already have SRTO_PEERIDLETIMEO set + case SRTO_OHEADBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_OHEADBW)); + break; - const int idletmo = m_iOPT_PeerIdleTimeout; + case SRTO_LOSSMAXTTL: + m_iReorderTolerance = m_config.m_iMaxReorderTolerance; - // Both are in milliseconds. - // This option is RECORDED in microseconds, while - // idletmp is recorded in milliseconds, only translated to - // microseconds directly before use. - if (val >= idletmo) - { - LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val - << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + default: break; } - - m_uOPT_StabilityTimeout = val * 1000; } - break; -#endif - - case SRTO_RETRANSMITALGO: - if (m_bConnected) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - - m_iOPT_RetransmitAlgo = cast_optval(optval, optlen); - break; - - default: - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } } @@ -994,17 +366,17 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) switch (optName) { case SRTO_MSS: - *(int *)optval = m_iMSS; + *(int *)optval = m_config.m_iMSS; optlen = sizeof(int); break; case SRTO_SNDSYN: - *(bool *)optval = m_bSynSending; + *(bool *)optval = m_config.m_bSynSending; optlen = sizeof(bool); break; case SRTO_RCVSYN: - *(bool *)optval = m_bSynRecving; + *(bool *)optval = m_config.m_bSynRecving; optlen = sizeof(bool); break; @@ -1014,17 +386,17 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_FC: - *(int *)optval = m_iFlightFlagSize; + *(int *)optval = m_config.m_iFlightFlagSize; optlen = sizeof(int); break; case SRTO_SNDBUF: - *(int *)optval = m_iSndBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.m_iSndBufSize * (m_config.m_iMSS - CPacket::UDP_HDR_SIZE); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_iRcvBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.m_iRcvBufSize * (m_config.m_iMSS - CPacket::UDP_HDR_SIZE); optlen = sizeof(int); break; @@ -1032,52 +404,52 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (optlen < (int)(sizeof(linger))) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - *(linger *)optval = m_Linger; + *(linger *)optval = m_config.m_Linger; optlen = sizeof(linger); break; case SRTO_UDP_SNDBUF: - *(int *)optval = m_iUDPSndBufSize; + *(int *)optval = m_config.m_iUDPSndBufSize; optlen = sizeof(int); break; case SRTO_UDP_RCVBUF: - *(int *)optval = m_iUDPRcvBufSize; + *(int *)optval = m_config.m_iUDPRcvBufSize; optlen = sizeof(int); break; case SRTO_RENDEZVOUS: - *(bool *)optval = m_bRendezvous; + *(bool *)optval = m_config.m_bRendezvous; optlen = sizeof(bool); break; case SRTO_SNDTIMEO: - *(int *)optval = m_iSndTimeOut; + *(int *)optval = m_config.m_iSndTimeOut; optlen = sizeof(int); break; case SRTO_RCVTIMEO: - *(int *)optval = m_iRcvTimeOut; + *(int *)optval = m_config.m_iRcvTimeOut; optlen = sizeof(int); break; case SRTO_REUSEADDR: - *(bool *)optval = m_bReuseAddr; + *(bool *)optval = m_config.m_bReuseAddr; optlen = sizeof(bool); break; case SRTO_MAXBW: - *(int64_t *)optval = m_llMaxBW; + *(int64_t *)optval = m_config.m_llMaxBW; optlen = sizeof(int64_t); break; case SRTO_INPUTBW: - *(int64_t*)optval = m_llInputBW; + *(int64_t*)optval = m_config.m_llInputBW; optlen = sizeof(int64_t); break; case SRTO_OHEADBW: - *(int32_t *)optval = m_iOverheadBW; + *(int32_t *)optval = m_config.m_iOverheadBW; optlen = sizeof(int32_t); break; @@ -1097,7 +469,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady()) event |= SRT_EPOLL_IN; leaveCS(m_RecvLock); - if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) + if (m_pSndBuffer && (m_config.m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) event |= SRT_EPOLL_OUT; } *(int32_t *)optval = event; @@ -1129,7 +501,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bOpened) *(int32_t *)optval = m_pSndQueue->getIpTTL(); else - *(int32_t *)optval = m_iIpTTL; + *(int32_t *)optval = m_config.m_iIpTTL; optlen = sizeof(int32_t); break; @@ -1137,7 +509,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bOpened) *(int32_t *)optval = m_pSndQueue->getIpToS(); else - *(int32_t *)optval = m_iIpToS; + *(int32_t *)optval = m_config.m_iIpToS; optlen = sizeof(int32_t); break; @@ -1153,8 +525,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) } // Fallback: return from internal data - strcpy(((char*)optval), m_BindToDevice.c_str()); - optlen = m_BindToDevice.size(); + strcpy(((char*)optval), m_config.m_BindToDevice.c_str()); + optlen = m_config.m_BindToDevice.size(); #else LOGC(smlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -1162,23 +534,30 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SENDER: - *(int32_t *)optval = m_bDataSender; + *(int32_t *)optval = m_config.m_bDataSender; optlen = sizeof(int32_t); break; case SRTO_TSBPDMODE: - *(int32_t *)optval = m_bOPT_TsbPd; + *(int32_t *)optval = m_config.m_bTSBPD; optlen = sizeof(int32_t); break; case SRTO_LATENCY: case SRTO_RCVLATENCY: - *(int32_t *)optval = m_iTsbPdDelay_ms; + if (m_bConnected) + *(int32_t *)optval = m_iTsbPdDelay_ms; + else + *(int32_t *)optval = m_config.m_iRcvLatency; optlen = sizeof(int32_t); break; case SRTO_PEERLATENCY: - *(int32_t *)optval = m_iPeerTsbPdDelay_ms; + if (m_bConnected) + *(int32_t *)optval = m_iPeerTsbPdDelay_ms; + else + *(int32_t *)optval = m_config.m_iPeerLatency; + optlen = sizeof(int32_t); break; @@ -1188,7 +567,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SNDDROPDELAY: - *(int32_t *)optval = m_iOPT_SndDropDelay; + *(int32_t *)optval = m_config.m_iSndDropDelay; optlen = sizeof(int32_t); break; @@ -1196,14 +575,14 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_pCryptoControl) *(int32_t *)optval = (int32_t) m_pCryptoControl->KeyLen(); // Running Key length. else - *(int32_t *)optval = m_iSndCryptoKeyLen; // May be 0. + *(int32_t *)optval = m_config.m_iSndCryptoKeyLen; // May be 0. optlen = sizeof(int32_t); break; case SRTO_KMSTATE: if (!m_pCryptoControl) *(int32_t *)optval = SRT_KM_S_UNSECURED; - else if (m_bDataSender) + else if (m_config.m_bDataSender) *(int32_t *)optval = m_pCryptoControl->m_SndKmState; else *(int32_t *)optval = m_pCryptoControl->m_RcvKmState; @@ -1227,17 +606,17 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_LOSSMAXTTL: - *(int32_t*)optval = m_iMaxReorderTolerance; + *(int32_t*)optval = m_config.m_iMaxReorderTolerance; optlen = sizeof(int32_t); break; case SRTO_NAKREPORT: - *(bool *)optval = m_bRcvNakReport; + *(bool *)optval = m_config.m_bRcvNakReport; optlen = sizeof(bool); break; case SRTO_VERSION: - *(int32_t *)optval = m_lSrtVersion; + *(int32_t *)optval = m_config.m_lSrtVersion; optlen = sizeof(int32_t); break; @@ -1247,50 +626,47 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_CONNTIMEO: - *(int*)optval = count_milliseconds(m_tdConnTimeOut); + *(int*)optval = count_milliseconds(m_config.m_tdConnTimeOut); optlen = sizeof(int); break; case SRTO_DRIFTTRACER: - *(int*)optval = m_bDriftTracer; + *(int*)optval = m_config.m_bDriftTracer; optlen = sizeof(int); break; case SRTO_MINVERSION: - *(uint32_t *)optval = m_lMinimumPeerSrtVersion; + *(uint32_t *)optval = m_config.m_lMinimumPeerSrtVersion; optlen = sizeof(uint32_t); break; case SRTO_STREAMID: - if (size_t(optlen) < m_sStreamName.size() + 1) + if (size_t(optlen) < m_config.m_StreamName.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_sStreamName.c_str()); - optlen = (int) m_sStreamName.size(); + strcpy((char *)optval, m_config.m_StreamName.c_str()); + optlen = (int) m_config.m_StreamName.size(); break; case SRTO_CONGESTION: - { - string tt = m_CongCtl.selected_name(); - strcpy((char *)optval, tt.c_str()); - optlen = (int) tt.size(); - } - break; + strcpy((char *)optval, m_config.m_Congestion.c_str()); + optlen = (int) m_config.m_Congestion.size(); + break; case SRTO_MESSAGEAPI: optlen = sizeof(bool); - *(bool *)optval = m_bMessageAPI; + *(bool *)optval = m_config.m_bMessageAPI; break; case SRTO_PAYLOADSIZE: optlen = sizeof(int); - *(int *)optval = (int) m_zOPT_ExpPayloadSize; + *(int *)optval = (int) m_config.m_zExpPayloadSize; break; #if ENABLE_EXPERIMENTAL_BONDING case SRTO_GROUPCONNECT: optlen = sizeof (int); - *(int*)optval = m_OPT_GroupConnect; + *(int*)optval = m_config.m_GroupConnect; break; case SRTO_GROUPTYPE: @@ -1301,29 +677,29 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_ENFORCEDENCRYPTION: optlen = sizeof(int32_t); // also with TSBPDMODE and SENDER - *(int32_t *)optval = m_bOPT_StrictEncryption; + *(int32_t *)optval = m_config.m_bEnforcedEnc; break; case SRTO_IPV6ONLY: optlen = sizeof(int); - *(int *)optval = m_iIpV6Only; + *(int *)optval = m_config.m_iIpV6Only; break; case SRTO_PEERIDLETIMEO: - *(int *)optval = m_iOPT_PeerIdleTimeout; + *(int *)optval = m_config.m_iPeerIdleTimeout; optlen = sizeof(int); break; case SRTO_PACKETFILTER: - if (size_t(optlen) < m_OPT_PktFilterConfigString.size() + 1) + if (size_t(optlen) < m_config.m_PacketFilterConfig.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_OPT_PktFilterConfigString.c_str()); - optlen = (int) m_OPT_PktFilterConfigString.size(); + strcpy((char *)optval, m_config.m_PacketFilterConfig.c_str()); + optlen = (int) m_config.m_PacketFilterConfig.size(); break; case SRTO_RETRANSMITALGO: - *(int32_t *)optval = m_iOPT_RetransmitAlgo; + *(int32_t *)optval = m_config.m_iRetransmitAlgo; optlen = sizeof(int32_t); break; @@ -1332,71 +708,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) } } -#if ENABLE_EXPERIMENTAL_BONDING -bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t optlen) -{ - // Check first if this option is allowed to be set - // as on a member socket. - - switch (optname) - { - case SRTO_BINDTODEVICE: - case SRTO_CONNTIMEO: - case SRTO_DRIFTTRACER: - //SRTO_FC - not allowed to be different among group members - case SRTO_GROUPSTABTIMEO: - //SRTO_INPUTBW - per transmission setting - case SRTO_IPTOS: - case SRTO_IPTTL: - case SRTO_KMREFRESHRATE: - case SRTO_KMPREANNOUNCE: - //SRTO_LATENCY - per transmission setting - //SRTO_LINGER - not for managed sockets - case SRTO_LOSSMAXTTL: - //SRTO_MAXBW - per transmission setting - //SRTO_MESSAGEAPI - groups are live mode only - //SRTO_MINVERSION - per group connection setting - case SRTO_NAKREPORT: - //SRTO_OHEADBW - per transmission setting - //SRTO_PACKETFILTER - per transmission setting - //SRTO_PASSPHRASE - per group connection setting - //SRTO_PASSPHRASE - per transmission setting - //SRTO_PBKEYLEN - per group connection setting - case SRTO_PEERIDLETIMEO: - case SRTO_RCVBUF: - //SRTO_RCVSYN - must be always false in groups - //SRTO_RCVTIMEO - must be alwyas -1 in groups - case SRTO_SNDBUF: - case SRTO_SNDDROPDELAY: - //SRTO_TLPKTDROP - per transmission setting - //SRTO_TSBPDMODE - per transmission setting - case SRTO_UDP_RCVBUF: - case SRTO_UDP_SNDBUF: - break; - - default: - // Other options are not allowed - return false; - - } - - // Header size will get the size likely aligned, but it won't - // hurt if the memory size will be up to 4 bytes more than - // needed - and it's better to not risk that alighment rules - // will make these calculations result in less space than needed. - const size_t headersize = sizeof(SingleOption); - const size_t payload = min(sizeof(uint32_t), optlen); - unsigned char* mem = new unsigned char[headersize + payload]; - SingleOption* option = reinterpret_cast(mem); - option->option = optname; - option->length = (uint16_t) optlen; - memcpy(option->storage, optval, optlen); - - options.push_back(option); - - return true; -} +#if ENABLE_EXPERIMENTAL_BONDING SRT_ERRNO CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) { SRT_SOCKOPT this_opt = SRTO_VERSION; @@ -1417,13 +730,13 @@ bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) if (!that) return false; - if (sid.size() > MAX_SID_LENGTH) + if (sid.size() > CSrtConfig::MAX_SID_LENGTH) return false; if (that->m_bConnected) return false; - that->m_sStreamName = sid; + that->m_config.m_StreamName.set(sid); return true; } @@ -1433,7 +746,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) if (!that) return ""; - return that->m_sStreamName; + return that->m_config.m_StreamName.str(); } // XXX REFACTOR: Make common code for CUDT constructor and clearData, @@ -1441,7 +754,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) void CUDT::clearData() { // Initial sequence number, loss, acknowledgement, etc. - int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -1513,17 +826,17 @@ void CUDT::clearData() // TSBPD as state should be set to FALSE here. // Only when the HSREQ handshake is exchanged, // should they be set to possibly true. - m_bTsbPd = false; - m_bGroupTsbPd = false; - m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; - m_bTLPktDrop = m_bOPT_TLPktDrop; + m_bTsbPd = false; + m_bGroupTsbPd = false; + m_iTsbPdDelay_ms = m_config.m_iRcvLatency; + m_bTLPktDrop = m_config.m_bTLPktDrop; m_bPeerTLPktDrop = false; m_bPeerNakReport = false; m_bPeerRexmitFlag = false; - m_RdvState = CHandShake::RDV_INVALID; + m_RdvState = CHandShake::RDV_INVALID; m_tsRcvPeerStartTime = steady_clock::time_point(); } @@ -1612,7 +925,7 @@ size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, memset((aw_srtdata), 0, sizeof(uint32_t) * srtlen); /* Current version (1.x.x) SRT handshake */ - aw_srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ + aw_srtdata[SRT_HS_VERSION] = m_config.m_lSrtVersion; /* Required version */ aw_srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); switch (msgtype) @@ -1639,10 +952,10 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu // not set TsbPd mode, it will simply ignore the proposed latency (PeerTsbPdDelay), although // if it has received the Rx latency as well, it must honor it and respond accordingly // (the latter is only in case of HSv5 and bidirectional connection). - if (m_bOPT_TsbPd) + if (m_config.m_bTSBPD) { - m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; - m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay; + m_iTsbPdDelay_ms = m_config.m_iRcvLatency; + m_iPeerTsbPdDelay_ms = m_config.m_iPeerLatency; /* * Sent data is real-time, use Time-based Packet Delivery, * set option bit and configured delay @@ -1671,7 +984,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu } } - if (m_bRcvNakReport) + if (m_config.m_bRcvNakReport) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; // I support SRT_OPT_REXMITFLG. Do you? @@ -1679,7 +992,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu // Declare the API used. The flag is set for "stream" API because // the older versions will never set this flag, but all old SRT versions use message API. - if (!m_bMessageAPI) + if (!m_config.m_bMessageAPI) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; HLOGC(cnlog.Debug, @@ -1750,7 +1063,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu if (m_bTLPktDrop) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; - if (m_bRcvNakReport) + if (m_config.m_bRcvNakReport) { // HSv5: Note that this setting is independent on the value of // m_bPeerNakReport, which represent this setting in the peer. @@ -1768,7 +1081,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu aw_srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; } - if (m_lSrtVersion >= SrtVersion(1, 2, 0)) + if (m_config.m_lSrtVersion >= SrtVersion(1, 2, 0)) { if (!m_bPeerRexmitFlag) { @@ -2068,12 +1381,12 @@ bool CUDT::createSrtHandshake( // // Keep 0 in the SRT_HSTYPE_HSFLAGS field, but still advertise PBKEYLEN // in the SRT_HSTYPE_ENCFLAGS field. - w_hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_iSndCryptoKeyLen); + w_hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_config.m_iSndCryptoKeyLen); - IF_HEAVY_LOGGING(bool whether = m_iSndCryptoKeyLen != 0); + IF_HEAVY_LOGGING(bool whether = m_config.m_iSndCryptoKeyLen != 0); HLOGC(cnlog.Debug, log << "createSrtHandshake: " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); // Note: This is required only when sending a HS message without SRT extensions. // When this is to be sent with SRT extensions, then KMREQ will be attached here @@ -2117,7 +1430,7 @@ bool CUDT::createSrtHandshake( bool have_sid = false; if (srths_cmd == SRT_CMD_HSREQ) { - if (m_sStreamName != "") + if (!m_config.m_StreamName.empty()) { have_sid = true; w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; @@ -2154,7 +1467,7 @@ bool CUDT::createSrtHandshake( // possibly confronted with the contents of m_OPT_FECConfigString, // and if it decided to go with filter, it will be nonempty. bool have_filter = false; - if (peer_filter_capable && m_OPT_PktFilterConfigString != "") + if (peer_filter_capable && !m_config.m_PacketFilterConfig.empty()) { have_filter = true; w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; @@ -2162,7 +1475,7 @@ bool CUDT::createSrtHandshake( } bool have_congctl = false; - const string& sm = m_CongCtl.selected_name(); + const string sm = m_config.m_Congestion.str(); if (sm != "" && sm != "live") { have_congctl = true; @@ -2177,7 +1490,7 @@ bool CUDT::createSrtHandshake( // KMRSP must be always sent when: // - Agent set a password, Peer did not send KMREQ: Agent sets snd=NOSECRET. // - Agent set no password, but Peer sent KMREQ: Ageng sets rcv=NOSECRET. - if (m_CryptoSecret.len > 0 || kmdata_wordsize > 0) + if (m_config.m_CryptoSecret.len > 0 || kmdata_wordsize > 0) { have_kmreq = true; w_hs.m_iType |= CHandShake::HS_EXT_KMREQ; @@ -2255,7 +1568,7 @@ bool CUDT::createSrtHandshake( // the conclusion packet. size_t size_limit = m_iMaxSRTPayloadSize / 2; - if (m_sStreamName.size() >= size_limit) + if (m_config.m_StreamName.size() >= size_limit) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Warn, @@ -2264,12 +1577,12 @@ bool CUDT::createSrtHandshake( } offset += ra_size + 1; - ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_sStreamName); + ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_config.m_StreamName.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after SID [" << m_sStreamName << "] length=" << m_sStreamName.size() - << " alignedln=" << (4*ra_size) << ": offset=" << offset << " SID size=" << ra_size - << " space left: " << (total_ra_size - offset)); + log << "createSrtHandshake: after SID [" << m_config.m_StreamName.c_str() + << "] length=" << m_config.m_StreamName.size() << " alignedln=" << (4 * ra_size) + << ": offset=" << offset << " SID size=" << ra_size << " space left: " << (total_ra_size - offset)); } if (have_congctl) @@ -2283,18 +1596,18 @@ bool CUDT::createSrtHandshake( HLOGC(cnlog.Debug, log << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size() - << " alignedln=" << (4*ra_size) << ": offset=" << offset << " CONGCTL size=" << ra_size + << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " CONGCTL size=" << ra_size << " space left: " << (total_ra_size - offset)); } if (have_filter) { offset += ra_size + 1; - ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_OPT_PktFilterConfigString); + ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_config.m_PacketFilterConfig.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after filter [" << m_OPT_PktFilterConfigString << "] length=" - << m_OPT_PktFilterConfigString.size() << " alignedln=" << (4*ra_size) << ": offset=" << offset + log << "createSrtHandshake: after filter [" << m_config.m_PacketFilterConfig.c_str() << "] length=" + << m_config.m_PacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " filter size=" << ra_size << " space left: " << (total_ra_size - offset)); } @@ -2345,7 +1658,7 @@ bool CUDT::createSrtHandshake( { HLOGC(cnlog.Debug, log << "createSrtHandshake: " - << (m_CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); + << (m_config.m_CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); if (srtkm_cmd == SRT_CMD_KMREQ) { bool have_any_keys = false; @@ -2520,7 +1833,7 @@ bool CUDT::processSrtMsg(const CPacket *ctrlpkt) { if (len_out == 1) { - if (m_bOPT_StrictEncryption) + if (m_config.m_bEnforcedEnc) { LOGC(cnlog.Warn, log << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])) @@ -2583,8 +1896,8 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t // Prepare the initial runtime values of latency basing on the option values. // They are going to get the value fixed HERE. - m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; - m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay; + m_iTsbPdDelay_ms = m_config.m_iRcvLatency; + m_iPeerTsbPdDelay_ms = m_config.m_iPeerLatency; if (bytelen < SRT_CMD_HSREQ_MINSZ) { @@ -2628,30 +1941,33 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t } // Check also if the version satisfies the minimum required version - if (m_lPeerSrtVersion < m_lMinimumPeerSrtVersion) + if (m_lPeerSrtVersion < m_config.m_lMinimumPeerSrtVersion) { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion) - << " is too old for requested: " << SrtVersionString(m_lMinimumPeerSrtVersion) << " - REJECTING"); + << " is too old for requested: " << SrtVersionString(m_config.m_lMinimumPeerSrtVersion) + << " - REJECTING"); return SRT_CMD_REJECT; } HLOGC(cnlog.Debug, log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_lPeerSrtVersion) << " Flags: " << m_lPeerSrtFlags - << "(" << SrtFlagString(m_lPeerSrtFlags) << ") Min req version:" << SrtVersionString(m_lMinimumPeerSrtVersion)); + << "(" << SrtFlagString(m_lPeerSrtFlags) + << ") Min req version:" << SrtVersionString(m_config.m_lMinimumPeerSrtVersion)); m_bPeerRexmitFlag = IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG); HLOGF(cnlog.Debug, "HSREQ/rcv: peer %s REXMIT flag", m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND"); // Check if both use the same API type. Reject if not. bool peer_message_api = !IsSet(m_lPeerSrtFlags, SRT_OPT_STREAM); - if (peer_message_api != m_bMessageAPI) + if (peer_message_api != m_config.m_bMessageAPI) { m_RejectReason = SRT_REJ_MESSAGEAPI; LOGC(cnlog.Error, - log << "HSREQ/rcv: Agent uses " << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, but the Peer declares " - << (peer_message_api ? "MESSAGE" : "STREAM") << " API. Not compatible transmission type, rejecting."); + log << "HSREQ/rcv: Agent uses " << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") + << " API, but the Peer declares " << (peer_message_api ? "MESSAGE" : "STREAM") + << " API. Not compatible transmission type, rejecting."); return SRT_CMD_REJECT; } @@ -2830,12 +2146,13 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t SrtFlagString(m_lPeerSrtFlags).c_str()); // Basic version check - if (m_lPeerSrtVersion < m_lMinimumPeerSrtVersion) + if (m_lPeerSrtVersion < m_config.m_lMinimumPeerSrtVersion) { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, log << "HSRSP/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion) - << " is too old for requested: " << SrtVersionString(m_lMinimumPeerSrtVersion) << " - REJECTING"); + << " is too old for requested: " << SrtVersionString(m_config.m_lMinimumPeerSrtVersion) + << " - REJECTING"); return SRT_CMD_REJECT; } @@ -2889,19 +2206,19 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t } } - if ((m_lSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) + if ((m_config.m_lSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) { // Too late packets dropping feature supported m_bPeerTLPktDrop = true; } - if ((m_lSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) + if ((m_config.m_lSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) { // Peer will send Periodic NAK Reports m_bPeerNakReport = true; } - if (m_lSrtVersion >= SrtVersion(1, 2, 0)) + if (m_config.m_lSrtVersion >= SrtVersion(1, 2, 0)) { if (IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG)) { @@ -3080,7 +2397,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, #ifdef SRT_ENABLE_ENCRYPTION if (!m_pCryptoControl->hasPassphrase()) { - if (m_bOPT_StrictEncryption) + if (m_config.m_bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -3131,7 +2448,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, { // This means that there was an abnormal encryption situation occurred. // This is inacceptable in case of strict encryption. - if (m_bOPT_StrictEncryption) + if (m_config.m_bEnforcedEnc) { if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADSECRET) { @@ -3151,7 +2468,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, else if (cmd == SRT_CMD_KMRSP) { int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); - if (m_bOPT_StrictEncryption && res == -1) + if (m_config.m_bEnforcedEnc && res == -1) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, log << "KMRSP failed - rejecting connection as per enforced encryption."); @@ -3178,7 +2495,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // When encryption is not enabled at compile time, behave as if encryption wasn't set, // so accordingly to StrictEncryption flag. - if (m_bOPT_StrictEncryption) + if (m_config.m_bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -3196,11 +2513,11 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, bool have_congctl = false; bool have_filter = false; - string agsm = m_CongCtl.selected_name(); + string agsm = m_config.m_Congestion.str(); if (agsm == "") { agsm = "live"; - m_CongCtl.select("live"); + m_config.m_Congestion.set("live", 4); } bool have_group ATR_UNUSED = false; @@ -3224,10 +2541,10 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, const size_t bytelen = blocklen * sizeof(uint32_t); if (cmd == SRT_CMD_SID) { - if (!bytelen || bytelen > MAX_SID_LENGTH) + if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH + log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } @@ -3239,16 +2556,16 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // padding zeros. In all these cases in the resulting array we should have all // subsequent characters of the string plus at least one '\0' at the end. This will // make it a perfect NUL-terminated string, to be used to initialize a string. - char target[MAX_SID_LENGTH + 1]; - memset((target), 0, MAX_SID_LENGTH + 1); + char target[CSrtConfig::MAX_SID_LENGTH + 1]; + memset((target), 0, CSrtConfig::MAX_SID_LENGTH + 1); memcpy((target), begin + 1, bytelen); // Un-swap on big endian machines ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); - m_sStreamName = target; + m_config.m_StreamName.set(target, bytelen); HLOGC(cnlog.Debug, - log << "CONNECTOR'S REQUESTED SID [" << m_sStreamName << "] (bytelen=" << bytelen + log << "CONNECTOR'S REQUESTED SID [" << m_config.m_StreamName.c_str() << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")"); } else if (cmd == SRT_CMD_CONGESTION) @@ -3260,18 +2577,18 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, return false; } - if (!bytelen || bytelen > MAX_SID_LENGTH) + if (!bytelen || bytelen > CSrtConfig::MAX_CONG_LENGTH) { LOGC(cnlog.Error, log << "interpretSrtHandshake: CONGESTION-control type length " << bytelen << " is 0 or > " - << +MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); + << +CSrtConfig::MAX_CONG_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } // Declare that congctl has been received have_congctl = true; - char target[MAX_SID_LENGTH + 1]; - memset((target), 0, MAX_SID_LENGTH + 1); + char target[CSrtConfig::MAX_CONG_LENGTH + 1]; + memset((target), 0, CSrtConfig::MAX_CONG_LENGTH + 1); memcpy((target), begin + 1, bytelen); // Un-swap on big endian machines ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); @@ -3301,14 +2618,19 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, LOGC(cnlog.Error, log << "FILTER BLOCK REPEATED!"); return false; } + + if (!bytelen || bytelen > CSrtConfig::MAX_PFILTER_LENGTH) + { + LOGC(cnlog.Error, + log << "interpretSrtHandshake: packet-filter type length " << bytelen << " is 0 or > " + << +CSrtConfig::MAX_PFILTER_LENGTH << " - PROTOCOL ERROR, REJECTING"); + return false; + } // Declare that filter has been received have_filter = true; - // XXX This is the maximum string, but filter config - // shall be normally limited somehow, especially if used - // together with SID! - char target[MAX_SID_LENGTH + 1]; - memset((target), 0, MAX_SID_LENGTH + 1); + char target[CSrtConfig::MAX_PFILTER_LENGTH + 1]; + memset((target), 0, CSrtConfig::MAX_PFILTER_LENGTH + 1); memcpy((target), begin + 1, bytelen); string fltcfg = target; @@ -3368,9 +2690,9 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // Post-checks // Check if peer declared encryption - if (!encrypted && m_CryptoSecret.len > 0) + if (!encrypted && m_config.m_CryptoSecret.len > 0) { - if (m_bOPT_StrictEncryption) + if (m_config.m_bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -3433,17 +2755,19 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) if (!PacketFilter::correctConfig(cfg)) return false; + string thisconf = m_config.m_PacketFilterConfig.str(); + // Now parse your own string, if you have it. - if (m_OPT_PktFilterConfigString != "") + if (thisconf != "") { - // - for rendezvous, both must be exactly the same, or only one side specified. - if (m_bRendezvous && m_OPT_PktFilterConfigString != confstr) + // - for rendezvous, both must be exactly the same (it's unspecified, which will be the first one) + if (m_config.m_bRendezvous && thisconf != confstr) { return false; } SrtFilterConfig mycfg; - if (!ParseFilterConfig(m_OPT_PktFilterConfigString, mycfg)) + if (!ParseFilterConfig(thisconf, mycfg)) return false; // Check only if both have set a filter of the same type. @@ -3482,24 +2806,24 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) myos << "," << x->first << ":" << x->second; } - m_OPT_PktFilterConfigString = myos.str(); + m_config.m_PacketFilterConfig.set(myos.str()); - HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Effective config: " << m_OPT_PktFilterConfigString); + HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Effective config: " << thisconf); } else { // Take the foreign configuration as a good deal. - HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Good deal config: " << m_OPT_PktFilterConfigString); - m_OPT_PktFilterConfigString = confstr; + HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Good deal config: " << thisconf); + m_config.m_PacketFilterConfig.set(confstr); } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; - if (m_zOPT_ExpPayloadSize > efc_max_payload_size) + if (m_config.m_zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, log << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " << efc_max_payload_size << " bytes"); - m_zOPT_ExpPayloadSize = efc_max_payload_size; + m_config.m_zExpPayloadSize = efc_max_payload_size; } return true; @@ -3521,7 +2845,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN int link_weight = SrtHSRequest::HS_GROUP_WEIGHT::unwrap(gd); uint32_t link_flags = SrtHSRequest::HS_GROUP_FLAGS::unwrap(gd); - if (m_OPT_GroupConnect == 0) + if (m_config.m_GroupConnect == 0) { m_RejectReason = SRT_REJ_GROUP; LOGC(cnlog.Error, log << "HS/GROUP: this socket is not allowed for group connect."); @@ -3742,8 +3066,8 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l } // Setting non-blocking reading for group socket. - s->core().m_bSynRecving = false; - s->core().m_bSynSending = false; + s->core().m_config.m_bSynRecving = false; + s->core().m_config.m_bSynSending = false; // Copy of addSocketToGroup. No idea how many parts could be common, not much. @@ -3871,7 +3195,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) ScopedLock cg (m_ConnectionLock); HLOGC(aclog.Debug, log << CONID() << "startConnect: -> " << serv_addr.str() - << (m_bSynRecving ? " (SYNCHRONOUS)" : " (ASYNCHRONOUS)") << "..."); + << (m_config.m_bSynRecving ? " (SYNCHRONOUS)" : " (ASYNCHRONOUS)") << "..."); if (!m_bOpened) throw CUDTException(MJ_NOTSUP, MN_NONE, 0); @@ -3888,9 +3212,9 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // register this socket in the rendezvous queue // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this // function - steady_clock::duration ttl = m_tdConnTimeOut; + steady_clock::duration ttl = m_config.m_tdConnTimeOut; - if (m_bRendezvous) + if (m_config.m_bRendezvous) ttl *= 10; const steady_clock::time_point ttl_time = steady_clock::now() + ttl; @@ -3904,7 +3228,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_ConnReq.m_iType = UDT_DGRAM; // This is my current configuration - if (m_bRendezvous) + if (m_config.m_bRendezvous) { // For rendezvous, use version 5 in the waveahand and the cookie. // In case when you get the version 4 waveahand, simply switch to @@ -3921,11 +3245,11 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // This will be also passed to a HSv4 rendezvous, but fortunately the old // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION. - m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_iSndCryptoKeyLen); - bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0; + m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_config.m_iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_config.m_iSndCryptoKeyLen != 0; HLOGC(aclog.Debug, log << "startConnect (rnd): " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); m_RdvState = CHandShake::RDV_WAVING; m_SrtHsSide = HSD_DRAW; // initially not resolved. } @@ -3946,8 +3270,10 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_RdvState = CHandShake::RDV_INVALID; } - m_ConnReq.m_iMSS = m_iMSS; - m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; + m_ConnReq.m_iMSS = m_config.m_iMSS; + // Defined as the size of the receiver buffer in packets, unless + // SRTO_FC has been set to a less value. + m_ConnReq.m_iFlightFlagSize = m_config.flightCapacity(); m_ConnReq.m_iID = m_SocketID; CIPAddress::ntop(serv_addr, (m_ConnReq.m_piPeerIP)); @@ -4020,7 +3346,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) ////////////////////////////////////////////////////// // SYNCHRO BAR ////////////////////////////////////////////////////// - if (!m_bSynRecving) + if (!m_config.m_bSynRecving) { HLOGC(cnlog.Debug, log << CONID() << "startConnect: ASYNC MODE DETECTED. Deferring the process to RcvQ:worker"); return; @@ -4057,7 +3383,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << "startConnect: LOOP: time to send (" << count_milliseconds(tdiff) << " > 250 ms). size=" << reqpkt.getLength()); - if (m_bRendezvous) + if (m_config.m_bRendezvous) reqpkt.m_iID = m_ConnRes.m_iID; now = steady_clock::now(); @@ -4218,7 +3544,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); e = CUDTException(MJ_SETUP, MN_REJECTED, 0); } - else if ((!m_bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check + else if ((!m_config.m_bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check e = CUDTException(MJ_SETUP, MN_SECURITY, 0); } @@ -4238,7 +3564,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << "startConnect: END. Parameters:" " mss=" - << m_iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() + << m_config.m_iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); } @@ -4282,7 +3608,7 @@ bool CUDT::processAsyncConnectRequest(EReadStatus rst, log << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_bRendezvous ? 0 : m_ConnRes.m_iID; + request.m_iID = !m_config.m_bRendezvous ? 0 : m_ConnRes.m_iID; bool status = true; @@ -4773,7 +4099,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // For HSv4, the data sender is INITIATOR, and the data receiver is RESPONDER, // regardless of the connecting side affiliation. This will be changed for HSv5. bool bidirectional = false; - HandshakeSide hsd = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + HandshakeSide hsd = m_config.m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; // (defined here due to 'goto' below). // SRT peer may send the SRT handshake private message (type 0x7fff) before a keep-alive. @@ -4787,7 +4113,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // For the initial form this value should not be checked. bool hsv5 = m_ConnRes.m_iVersion >= HS_VERSION_SRT1; - if (m_bRendezvous && + if (m_config.m_bRendezvous && (m_RdvState == CHandShake::RDV_CONNECTED // somehow Rendezvous-v5 switched it to CONNECTED. || !response.isControl() // WAS A PAYLOAD PACKET. || (response.getType() == UMSG_KEEPALIVE) // OR WAS A UMSG_KEEPALIVE message. @@ -4851,7 +4177,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // Yes, we do abort to prevent buffer overrun. Set your MSS correctly // and you'll avoid problems. m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Fatal, log << "MSS size " << m_iMSS << "exceeds MTU size!"); + LOGC(cnlog.Fatal, log << "MSS size " << m_config.m_iMSS << "exceeds MTU size!"); return CONN_REJECT; } @@ -4860,7 +4186,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // The CCryptoControl attached object must be created early // because it will be required to create a conclusion handshake in HSv5 // - if (m_bRendezvous) + if (m_config.m_bRendezvous) { // SANITY CHECK: A rendezvous socket should reject any caller requests (it's not a listener) if (m_ConnRes.m_iReqType == URQ_INDUCTION) @@ -4959,7 +4285,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti m_ConnReq.m_extension = true; // For HSv5, the caller is INITIATOR and the listener is RESPONDER. - // The m_bDataSender value should be completely ignored and the + // The m_config.m_bDataSender value should be completely ignored and the // connection is always bidirectional. bidirectional = true; hsd = HSD_INITIATOR; @@ -4991,9 +4317,9 @@ bool CUDT::applyResponseSettings() ATR_NOEXCEPT } // Re-configure according to the negotiated values. - m_iMSS = m_ConnRes.m_iMSS; + m_config.m_iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -5223,26 +4549,27 @@ void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t ty if (enc_flags >= 2 && enc_flags <= 4) // 2 = 128, 3 = 192, 4 = 256 { int rcv_pbkeylen = SrtHSRequest::SRT_PBKEYLEN_BITS::wrap(enc_flags); - if (m_iSndCryptoKeyLen == 0) + if (m_config.m_iSndCryptoKeyLen == 0) { - m_iSndCryptoKeyLen = rcv_pbkeylen; - HLOGC(cnlog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " << m_iSndCryptoKeyLen); + m_config.m_iSndCryptoKeyLen = rcv_pbkeylen; + HLOGC(cnlog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " + << m_config.m_iSndCryptoKeyLen); } - else if (m_iSndCryptoKeyLen != rcv_pbkeylen) + else if (m_config.m_iSndCryptoKeyLen != rcv_pbkeylen) { // Conflict. Use SRTO_SENDER flag to check if this side should accept // the enforcement, otherwise simply let it win. - if (!m_bDataSender) + if (!m_config.m_bDataSender) { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_iSndCryptoKeyLen << " by " + log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.m_iSndCryptoKeyLen << " by " << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)"); - m_iSndCryptoKeyLen = rcv_pbkeylen; + m_config.m_iSndCryptoKeyLen = rcv_pbkeylen; } else { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - keep " << m_iSndCryptoKeyLen + log << loghdr << ": PBKEYLEN conflict - keep " << m_config.m_iSndCryptoKeyLen << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER"); } } @@ -5735,7 +5062,7 @@ void *CUDT::tsbpd(void *param) * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available */ - if (self->m_bSynRecving) + if (self->m_config.m_bSynRecving) { recvdata_cc.signal_locked(recv_lock); } @@ -5876,17 +5203,17 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD } else { - hsd = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + hsd = m_config.m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; } } try { m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); - m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.m_iRcvBufSize); // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); - m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + m_pRcvLossList = new CRcvLossList(m_config.m_iFlightFlagSize); } catch (...) { @@ -5911,10 +5238,10 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) { // this is a reponse handshake - w_hs.m_iReqType = URQ_CONCLUSION; - w_hs.m_iMSS = m_iMSS; - w_hs.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; - w_hs.m_iID = m_SocketID; + w_hs.m_iReqType = URQ_CONCLUSION; + w_hs.m_iMSS = m_config.m_iMSS; + w_hs.m_iFlightFlagSize = m_config.flightCapacity(); + w_hs.m_iID = m_SocketID; if (w_hs.m_iVersion > HS_VERSION_UDT4) { @@ -5936,15 +5263,15 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly at SRT HS // Uses the smaller MSS between the peers - m_iMSS = std::min(m_iMSS, w_hs.m_iMSS); + m_config.m_iMSS = std::min(m_config.m_iMSS, w_hs.m_iMSS); // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; - m_iPeerISN = w_hs.m_iISN; + m_iPeerISN = w_hs.m_iISN; setInitialRcvSeq(m_iPeerISN); m_iRcvCurrPhySeqNo = CSeqNo::decseq(w_hs.m_iISN); - m_PeerID = w_hs.m_iID; + m_PeerID = w_hs.m_iID; // use peer's ISN and send it back for security check m_iISN = w_hs.m_iISN; @@ -5959,7 +5286,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, rewriteHandshakeData(peer, (w_hs)); - int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; HLOGC(cnlog.Debug, log << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -6117,12 +5444,12 @@ bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) // These data should probably be filled only upon // reception of the conclusion handshake - otherwise // they have outdated values. - m_pCryptoControl->setCryptoSecret(m_CryptoSecret); + m_pCryptoControl->setCryptoSecret(m_config.m_CryptoSecret); - if (bidirectional || m_bDataSender) + if (bidirectional || m_config.m_bDataSender) { - HLOGC(rslog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_iSndCryptoKeyLen); - m_pCryptoControl->setCryptoKeylen(m_iSndCryptoKeyLen); + HLOGC(rslog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_config.m_iSndCryptoKeyLen); + m_pCryptoControl->setCryptoKeylen(m_config.m_iSndCryptoKeyLen); } return m_pCryptoControl->init(side, bidirectional); @@ -6139,26 +5466,28 @@ SRT_REJECT_REASON CUDT::setupCC() // XXX Not sure about that. May happen that AGENT wants // tsbpd mode, but PEER doesn't, even in bidirectional mode. // This way, the reception side should get precedense. - // if (bidirectional || m_bDataSender || m_bTwoWayData) - // m_bPeerTsbPd = m_bOPT_TsbPd; + // if (bidirectional || m_config.m_bDataSender || m_bTwoWayData) + // m_bPeerTsbPd = m_bTSBPD; // SrtCongestion will retrieve whatever parameters it needs // from *this. - if (!m_CongCtl.configure(this)) + + bool res = m_CongCtl.select(m_config.m_Congestion.str()); + if (!res || !m_CongCtl.configure(this)) { return SRT_REJ_CONGESTION; } // Configure filter module - if (m_OPT_PktFilterConfigString != "") + if (!m_config.m_PacketFilterConfig.empty()) { // This string, when nonempty, defines that the corrector shall be // configured. Otherwise it's left uninitialized. // At this point we state everything is checked and the appropriate // corrector type is already selected, so now create it. - HLOGC(pflog.Debug, log << "filter: Configuring: " << m_OPT_PktFilterConfigString); - if (!m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_OPT_PktFilterConfigString)) + HLOGC(pflog.Debug, log << "filter: Configuring: " << m_config.m_PacketFilterConfig.c_str()); + if (!m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_config.m_PacketFilterConfig.str())) { return SRT_REJ_FILTER; } @@ -6187,7 +5516,7 @@ SRT_REJECT_REASON CUDT::setupCC() m_tsLastSndTime = currtime; HLOGC(rslog.Debug, - log << "setupCC: setting parameters: mss=" << m_iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize + log << "setupCC: setting parameters: mss=" << m_config.m_iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); @@ -6204,7 +5533,7 @@ void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) // Do a fast pre-check first - this simply declares that agent uses HSv5 // and the legacy SRT Handshake is not to be done. Second check is whether // agent is sender (=initiator in HSv4). - if (!isOPT_TsbPd() || !m_bDataSender) + if (!isOPT_TsbPd() || !m_config.m_bDataSender) return; if (m_iSndHsRetryCnt <= 0) @@ -6307,23 +5636,23 @@ bool CUDT::closeInternal() // that it's in response to a broken connection. HLOGC(smlog.Debug, log << CONID() << " - closing socket:"); - if (m_Linger.l_onoff != 0) + if (m_config.m_Linger.l_onoff != 0) { const steady_clock::time_point entertime = steady_clock::now(); HLOGC(smlog.Debug, log << CONID() << " ... (linger)"); while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && - (steady_clock::now() - entertime < seconds_from(m_Linger.l_linger))) + (steady_clock::now() - entertime < seconds_from(m_config.m_Linger.l_linger))) { // linger has been checked by previous close() call and has expired if (m_tsLingerExpiration >= entertime) break; - if (!m_bSynSending) + if (!m_config.m_bSynSending) { // if this socket enables asynchronous sending, return immediately and let GC to close it later if (is_zero(m_tsLingerExpiration)) - m_tsLingerExpiration = entertime + seconds_from(m_Linger.l_linger); + m_tsLingerExpiration = entertime + seconds_from(m_config.m_Linger.l_linger); HLOGC(smlog.Debug, log << "CUDT::close: linger-nonblocking, setting expire time T=" @@ -6454,9 +5783,7 @@ bool CUDT::closeInternal() m_pCryptoControl.reset(); leaveCS(m_RcvBufferLock); - m_lSrtVersion = SRT_DEF_VERSION; m_lPeerSrtVersion = SRT_VERSION_UNK; - m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1; m_tsRcvPeerStartTime = steady_clock::time_point(); m_bOpened = false; @@ -6502,7 +5829,7 @@ int CUDT::receiveBuffer(char *data, int len) return 0; } HLOGC(arlog.Debug, - log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + log << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6511,14 +5838,14 @@ int CUDT::receiveBuffer(char *data, int len) CSync tscond (m_RcvTsbPdCond, recvguard); if (!m_pRcvBuffer->isRcvDataReady()) { - if (!m_bSynRecving) + if (!m_config.m_bSynRecving) { throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } else { /* Kick TsbPd thread to schedule next wakeup (if running) */ - if (m_iRcvTimeOut < 0) + if (m_config.m_iRcvTimeOut < 0) { THREAD_PAUSED(); while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) @@ -6530,7 +5857,8 @@ int CUDT::receiveBuffer(char *data, int len) } else { - const steady_clock::time_point exptime = steady_clock::now() + milliseconds_from(m_iRcvTimeOut); + const steady_clock::time_point exptime = + steady_clock::now() + milliseconds_from(m_config.m_iRcvTimeOut); THREAD_PAUSED(); while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) { @@ -6549,13 +5877,13 @@ int CUDT::receiveBuffer(char *data, int len) if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { // See at the beginning - if (!m_bMessageAPI && m_bShutdown) + if (!m_config.m_bMessageAPI && m_bShutdown) { HLOGC(arlog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); return 0; } HLOGC(arlog.Debug, - log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + log << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6580,7 +5908,7 @@ int CUDT::receiveBuffer(char *data, int len) s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } - if ((res <= 0) && (m_iRcvTimeOut >= 0)) + if ((res <= 0) && (m_config.m_iRcvTimeOut >= 0)) throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); return res; @@ -6593,7 +5921,7 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) if (!m_bPeerTLPktDrop) return; - if (!m_bMessageAPI) + if (!m_config.m_bMessageAPI) { LOGC(aslog.Error, log << "The SRTO_TLPKTDROP flag can only be used with message API."); throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); @@ -6610,9 +5938,9 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) // picture rate would be useful in auto SRT setting for min latency // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable int threshold_ms = 0; - if (m_iOPT_SndDropDelay >= 0) + if (m_config.m_iSndDropDelay >= 0) { - threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_iOPT_SndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + + threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_config.m_iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + (2 * COMM_SYN_INTERVAL_US / 1000); } @@ -6730,7 +6058,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { SrtCongestion::TransAPI api = SrtCongestion::STA_MESSAGE; CodeMinor mn = MN_INVALMSGAPI; - if (!m_bMessageAPI) + if (!m_config.m_bMessageAPI) { api = SrtCongestion::STA_BUFFER; mn = MN_INVALBUFFERAPI; @@ -6758,11 +6086,11 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // out a message of a length that exceeds the total size of the sending // buffer (configurable by SRTO_SNDBUF). - if (m_bMessageAPI && len > int(m_iSndBufSize * m_iMaxSRTPayloadSize)) + if (m_config.m_bMessageAPI && len > int(m_config.m_iSndBufSize * m_iMaxSRTPayloadSize)) { LOGC(aslog.Error, log << "Message length (" << len << ") exceeds the size of sending buffer: " - << (m_iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); + << (m_config.m_iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); } @@ -6791,7 +6119,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) checkNeedDrop((bCongestion)); int minlen = 1; // Minimum sender buffer space required for STREAM API - if (m_bMessageAPI) + if (m_config.m_bMessageAPI) { // For MESSAGE API the minimum outgoing buffer space required is // the size that can carry over the whole message as passed here. @@ -6802,21 +6130,22 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { //>>We should not get here if SRT_ENABLE_TLPKTDROP // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop. - if (!m_bSynSending) + if (!m_config.m_bSynSending) throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); { // wait here during a blocking sending UniqueLock sendblock_lock (m_SendBlockLock); - if (m_iSndTimeOut < 0) + if (m_config.m_iSndTimeOut < 0) { while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) m_SendBlockCond.wait(sendblock_lock); } else { - const steady_clock::time_point exptime = steady_clock::now() + milliseconds_from(m_iSndTimeOut); + const steady_clock::time_point exptime = + steady_clock::now() + milliseconds_from(m_config.m_iSndTimeOut); THREAD_PAUSED(); while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) { @@ -6846,7 +6175,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) */ if (sndBuffersLeft() < minlen) { - if (m_iSndTimeOut >= 0) + if (m_config.m_iSndTimeOut >= 0) throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); // XXX This looks very weird here, however most likely @@ -6880,7 +6209,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) } int size = len; - if (!m_bMessageAPI) + if (!m_config.m_bMessageAPI) { // For STREAM API it's allowed to send less bytes than the given buffer. // Just return how many bytes were actually scheduled for writing. @@ -6895,7 +6224,8 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) int32_t seqno = m_iSndNextSeqNo; IF_HEAVY_LOGGING(int32_t orig_seqno = seqno); - IF_HEAVY_LOGGING(steady_clock::time_point ts_srctime = steady_clock::time_point() + microseconds_from(w_mctrl.srctime)); + IF_HEAVY_LOGGING(steady_clock::time_point ts_srctime = + steady_clock::time_point() + microseconds_from(w_mctrl.srctime)); // Check if seqno has been set, in case when this is a group sender. // If the sequence is from the past towards the "next sequence", @@ -6930,7 +6260,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI); } - if (w_mctrl.srctime && (!m_bMessageAPI || !m_bTsbPd)) + if (w_mctrl.srctime && (!m_config.m_bMessageAPI || !m_bTsbPd)) { HLOGC(aslog.Warn, log << "Source time can only be used with TSBPD and Message API enabled. Using default time instead."); @@ -7012,7 +6342,7 @@ int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (m_bMessageAPI) + if (m_config.m_bMessageAPI) return receiveMessage(data, len, (w_mctrl)); return receiveBuffer(data, len); @@ -7048,7 +6378,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep ptrn[pos[0]] = charbool[m_bBroken]; ptrn[pos[1]] = charbool[m_bConnected]; ptrn[pos[2]] = charbool[m_bClosing]; - ptrn[pos[3]] = charbool[m_bSynRecving]; + ptrn[pos[3]] = charbool[m_config.m_bSynRecving]; int wrtlen = sprintf(ptrn + pos[4], "%d", m_pRcvBuffer->getRcvMsgNum()); strcpy(ptrn + pos[4] + wrtlen, "\n"); fputs(ptrn, stderr); @@ -7081,7 +6411,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep if (res == 0) { - if (!m_bMessageAPI && m_bShutdown) + if (!m_config.m_bMessageAPI && m_bShutdown) return 0; // Forced to return error instead of throwing exception. if (!by_exception) @@ -7094,7 +6424,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep const int seqdistance = -1; - if (!m_bSynRecving) + if (!m_config.m_bSynRecving) { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len); enterCS(m_RcvBufferLock); @@ -7154,7 +6484,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep int res = 0; bool timeout = false; // Do not block forever, check connection status each 1 sec. - const steady_clock::duration recv_timeout = m_iRcvTimeOut < 0 ? seconds_from(1) : milliseconds_from(m_iRcvTimeOut); + const steady_clock::duration recv_timeout = m_config.m_iRcvTimeOut < 0 ? seconds_from(1) : milliseconds_from(m_config.m_iRcvTimeOut); CSync recv_cond (m_RecvDataCond, recvguard); @@ -7189,12 +6519,12 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep const steady_clock::time_point exptime = steady_clock::now() + recv_timeout; HLOGC(tslog.Debug, - log << CONID() << "receiveMessage: fall asleep up to TS=" << FormatTime(exptime) << " lock=" << (&m_RecvLock) - << " cond=" << (&m_RecvDataCond)); + log << CONID() << "receiveMessage: fall asleep up to TS=" << FormatTime(exptime) + << " lock=" << (&m_RecvLock) << " cond=" << (&m_RecvDataCond)); if (!recv_cond.wait_until(exptime)) { - if (m_iRcvTimeOut >= 0) // otherwise it's "no timeout set" + if (m_config.m_iRcvTimeOut >= 0) // otherwise it's "no timeout set" timeout = true; HLOGP(tslog.Debug, "receiveMessage: DATA COND: EXPIRED -- checking connection conditions and rolling again"); @@ -7227,7 +6557,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // Forced to return 0 instead of throwing exception. if (!by_exception) return APIError(MJ_CONNECTION, MN_CONNLOST, 0); - if (!m_bMessageAPI && m_bShutdown) + if (!m_config.m_bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -7263,7 +6593,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // Unblock when required // LOGC(tslog.Debug, "RECVMSG/EXIT RES " << res << " RCVTIMEOUT"); - if ((res <= 0) && (m_iRcvTimeOut >= 0)) + if ((res <= 0) && (m_config.m_iRcvTimeOut >= 0)) { // Forced to return -1 instead of throwing exception. if (!by_exception) @@ -7402,7 +6732,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { - if (!m_bMessageAPI && m_bShutdown) + if (!m_config.m_bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -7491,7 +6821,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { - if (!m_bMessageAPI && m_bShutdown) + if (!m_config.m_bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -7599,19 +6929,21 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; - double interval = count_microseconds(currtime - m_stats.tsLastSampleTime); - perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; - perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; + double interval = count_microseconds(currtime - m_stats.tsLastSampleTime); + perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; + perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; perf->usPktSndPeriod = count_microseconds(m_tdSendInterval); perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); perf->msRTT = (double)m_iRTT / 1000.0; - perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; - perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; - perf->byteMSS = m_iMSS; + perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; + perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; + perf->byteMSS = m_config.m_iMSS; - perf->mbpsMaxBW = m_llMaxBW > 0 ? Bps2Mbps(m_llMaxBW) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; + perf->mbpsMaxBW = m_config.m_llMaxBW > 0 ? Bps2Mbps(m_config.m_llMaxBW) + : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) + : 0; const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth; @@ -7633,7 +6965,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) } perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize); //< - perf->byteAvailSndBuf = (m_iSndBufSize - perf->pktSndBuf) * m_iMSS; + perf->byteAvailSndBuf = (m_config.m_iSndBufSize - perf->pktSndBuf) * m_config.m_iMSS; } else { @@ -7645,7 +6977,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_pRcvBuffer) { - perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_iMSS; + perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_config.m_iMSS; if (instantaneous) // no need for historical API for Rcv side { perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); @@ -7733,24 +7065,24 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) EInitEvent only_input = arg.get(); // false = TEV_INIT_RESET: in the beginning, or when MAXBW was changed. - if (only_input && m_llMaxBW) + if (only_input && m_config.m_llMaxBW) { - HLOGC(rslog.Debug, log << CONID() << "updateCC/TEV_INIT: non-RESET stage and m_llMaxBW already set to " << m_llMaxBW); + HLOGC(rslog.Debug, log << CONID() << "updateCC/TEV_INIT: non-RESET stage and m_config.m_llMaxBW already set to " << m_config.m_llMaxBW); // Don't change } - else // either m_llMaxBW == 0 or only_input == TEV_INIT_RESET + else // either m_config.m_llMaxBW == 0 or only_input == TEV_INIT_RESET { // Use the values: // - if SRTO_MAXBW is >0, use it. // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling // Bytes/s - int bw = m_llMaxBW != 0 ? m_llMaxBW : // When used SRTO_MAXBW - m_llInputBW != 0 ? withOverhead(m_llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW + int bw = m_config.m_llMaxBW != 0 ? m_config.m_llMaxBW : // When used SRTO_MAXBW + m_config.m_llInputBW != 0 ? withOverhead(m_config.m_llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling // Note: setting bw == 0 uses BW_INFINITE value in LiveCC - m_CongCtl->updateBandwidth(m_llMaxBW, bw); + m_CongCtl->updateBandwidth(m_config.m_llMaxBW, bw); if (only_input == TEV_INIT_OHEADBW) { @@ -7765,7 +7097,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) } HLOGC(rslog.Debug, - log << CONID() << "updateCC/TEV_INIT: updating BW=" << m_llMaxBW + log << CONID() << "updateCC/TEV_INIT: updating BW=" << m_config.m_llMaxBW << (only_input == TEV_INIT_RESET ? " (UNCHANGED)" : only_input == TEV_INIT_OHEADBW ? " (only Overhead)" : " (updated sampling rate)")); @@ -7778,7 +7110,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) { // Specific part done when MaxBW is set to 0 (auto) and InputBW is 0. // This requests internal input rate sampling. - if (m_llMaxBW == 0 && m_llInputBW == 0) + if (m_config.m_llMaxBW == 0 && m_config.m_llInputBW == 0) { // Get auto-calculated input rate, Bytes per second const int64_t inputbw = m_pSndBuffer->getInputRate(); @@ -8207,7 +7539,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } else { - if (m_bSynRecving) + if (m_config.m_bSynRecving) { // signal a waiting "recv" call if there is any data available CSync::lock_signal(m_RecvDataCond, m_RecvLock); @@ -8391,7 +7723,7 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // insert this socket to snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); - if (m_bSynSending) + if (m_config.m_bSynSending) { CSync::lock_signal(m_SendBlockCond, m_SendBlockLock); } @@ -8800,7 +8132,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // inaccurate. Additionally it won't lock if TSBPD mode is off, and // won't update anything. Note that if you set TSBPD mode and use // srt_recvfile (which doesn't make any sense), you'll have a deadlock. - if (m_bDriftTracer) + if (m_config.m_bDriftTracer) { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; @@ -8853,7 +8185,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? - || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION + || (m_config.m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION { // The peer side has not received the handshake message, so it keeps querying // resend the handshake packet @@ -8865,12 +8197,12 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // - this is any of URQ_ERROR_* - well... CHandShake initdata; initdata.m_iISN = m_iISN; - initdata.m_iMSS = m_iMSS; - initdata.m_iFlightFlagSize = m_iFlightFlagSize; + initdata.m_iMSS = m_config.m_iMSS; + initdata.m_iFlightFlagSize = m_config.m_iFlightFlagSize; // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. - initdata.m_iReqType = (!m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; + initdata.m_iReqType = (!m_config.m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; initdata.m_iID = m_SocketID; uint32_t kmdata[SRTDATA_MAXSIZE]; @@ -8900,7 +8232,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // and should be added with HSRSP/KMRSP), or it's a belated handshake // of Rendezvous when it has already considered itself connected. // Sanity check - according to the rules, there should be no such situation - if (m_bRendezvous && m_SrtHsSide == HSD_RESPONDER) + if (m_config.m_bRendezvous && m_SrtHsSide == HSD_RESPONDER) { LOGC(inlog.Error, log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " @@ -9202,7 +8534,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime continue; } - if (m_bPeerNakReport && m_iOPT_RetransmitAlgo != 0) + if (m_bPeerNakReport && m_config.m_iRetransmitAlgo != 0) { const steady_clock::time_point tsLastRexmit = m_pSndBuffer->getPacketRexmitTime(offset); if (tsLastRexmit >= time_nak) @@ -10443,7 +9775,7 @@ void CUDT::unlose(const CPacket &packet) leaveCS(m_StatsLock); if (seqdiff > m_iReorderTolerance) { - const int new_tolerance = min(seqdiff, m_iMaxReorderTolerance); + const int new_tolerance = min(seqdiff, m_config.m_iMaxReorderTolerance); HLOGF(qrlog.Debug, "Belated by %d seqs - Reorder tolerance %s %d", seqdiff, @@ -10761,11 +10093,11 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // Additionally, set this field to a MAGIC value. This field isn't used during INDUCTION // by HSv4 client, HSv5 client can use it to additionally verify that this is a HSv5 listener. // In this field we also advertise the PBKEYLEN value. When 0, it's considered not advertised. - hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_iSndCryptoKeyLen); - bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0; + hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.m_iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_config.m_iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, log << "processConnectRequest: " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); size_t size = packet.getLength(); hs.store_to((packet.m_pcData), (size)); @@ -11063,11 +10395,11 @@ int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) // by the filter. By this reason they appear often out of order // and for adding them properly the loss list container wasn't // prepared. This then requires some more effort to implement. - if (!m_bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS) + if (!m_config.m_bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS) return BECAUSE_NO_REASON; /* - * m_bRcvNakReport enables NAK reports for SRT. + * m_config.m_bRcvNakReport enables NAK reports for SRT. * Retransmission based on timeout is bandwidth consuming, * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). @@ -11136,7 +10468,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea return false; // ms -> us - const int PEER_IDLE_TMO_US = m_iOPT_PeerIdleTimeout * 1000; + const int PEER_IDLE_TMO_US = m_config.m_iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && @@ -11390,7 +10722,7 @@ void CUDT::addEPoll(const int eid) } leaveCS(m_RecvLock); - if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) + if (m_config.m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) { s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } @@ -11495,8 +10827,8 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs // Prepare the information for the hook. // We need streamid. - char target[MAX_SID_LENGTH + 1]; - memset((target), 0, MAX_SID_LENGTH + 1); + char target[CSrtConfig::MAX_SID_LENGTH + 1]; + memset((target), 0, CSrtConfig::MAX_SID_LENGTH + 1); // Just for a case, check the length. // This wasn't done before, and we could risk memory crash. @@ -11527,10 +10859,10 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs if (cmd == SRT_CMD_SID) { - if (!bytelen || bytelen > MAX_SID_LENGTH) + if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH + log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } @@ -11565,14 +10897,14 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs } #if ENABLE_EXPERIMENTAL_BONDING - if (have_group && acore->m_OPT_GroupConnect == 0) + if (have_group && acore->m_config.m_GroupConnect == 0) { HLOGC(cnlog.Debug, log << "runAcceptHook: REJECTING connection WITHOUT calling the hook - groups not allowed"); return false; } // Update the groupconnect flag - acore->m_OPT_GroupConnect = have_group ? 1 : 0; + acore->m_config.m_GroupConnect = have_group ? 1 : 0; acore->m_HSGroupType = gt; #endif diff --git a/srtcore/core.h b/srtcore/core.h index 8c0a1a85e..6ed5d02e0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -69,6 +69,7 @@ modified by #include "handshake.h" #include "congctl.h" #include "packetfilter.h" +#include "socketconfig.h" #include "utilities.h" #include "logger_defs.h" @@ -139,71 +140,9 @@ enum SeqPairItems }; #if ENABLE_EXPERIMENTAL_BONDING - -struct SRT_SocketOptionObject -{ - struct SingleOption - { - uint16_t option; - uint16_t length; - unsigned char storage[1]; // NOTE: Variable length object! - }; - - std::vector options; - - SRT_SocketOptionObject() {} - - ~SRT_SocketOptionObject() - { - for (size_t i = 0; i < options.size(); ++i) - { - // Convert back - unsigned char* mem = reinterpret_cast(options[i]); - delete [] mem; - } - } - - bool add(SRT_SOCKOPT optname, const void* optval, size_t optlen); -}; - class CUDTGroup; #endif -template -inline T cast_optval(const void* optval) -{ - return *reinterpret_cast(optval); -} - -template -inline T cast_optval(const void* optval, int optlen) -{ - if (optlen > 0 && optlen != sizeof(T)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - return cast_optval(optval); -} - -// This function is to make it possible for both C and C++ -// API to accept both bool and int types for boolean options. -// (it's not that C couldn't use , it's that people -// often forget to use correct type). -template <> -inline bool cast_optval(const void* optval, int optlen) -{ - if (optlen == sizeof(bool)) - { - return *reinterpret_cast(optval); - } - - if (optlen == sizeof(int)) - { - // 0!= is a windows warning-killer int-to-bool conversion - return 0 != *reinterpret_cast(optval); - } - return false; -} - // Extended SRT Congestion control class - only an incomplete definition required class CCryptoControl; class CUDTUnited; @@ -330,24 +269,13 @@ class CUDT // Note: use notation with X*1000*1000* ... instead of million zeros in a row. // In C++17 there is a possible notation of 5'000'000 for convenience, but that's // something only for a far future. - static const int COMM_RESPONSE_TIMEOUT_MS = 5*1000; // 5 seconds static const int COMM_RESPONSE_MAX_EXP = 16; static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const uint32_t COMM_DEF_STABILITY_TIMEOUT_US = 80*1000; static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; static const uint16_t MAX_WEIGHT = 32767; - static const int - DEF_MSS = 1500, - DEF_FLIGHT_SIZE = 25600, - DEF_BUFFER_SIZE = 8192, //Rcv buffer MUST NOT be bigger than Flight Flag size - DEF_LINGER_S = 3*60, // 3 minutes - DEF_UDP_BUFFER_SIZE = 65536, - DEF_CONNTIMEO_S = 3; // 3 seconds - - int handshakeVersion() { return m_ConnRes.m_iVersion; @@ -372,7 +300,7 @@ class CUDT void addressAndSend(CPacket& pkt); void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); - bool isOPT_TsbPd() const { return m_bOPT_TsbPd; } + bool isOPT_TsbPd() const { return m_config.m_bTSBPD; } int RTT() const { return m_iRTT; } int RTTVar() const { return m_iRTTVar; } int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } @@ -385,13 +313,13 @@ class CUDT int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } - int64_t maxBandwidth() const { return m_llMaxBW; } - int MSS() const { return m_iMSS; } + int64_t maxBandwidth() const { return m_config.m_llMaxBW; } + int MSS() const { return m_config.m_iMSS; } uint32_t peerLatency_us() const {return m_iPeerTsbPdDelay_ms * 1000; } - int peerIdleTimeout_ms() const { return m_iOPT_PeerIdleTimeout; } + int peerIdleTimeout_ms() const { return m_config.m_iPeerIdleTimeout; } size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } - size_t OPT_PayloadSize() const { return m_zOPT_ExpPayloadSize; } + size_t OPT_PayloadSize() const { return m_config.m_zExpPayloadSize; } int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -436,9 +364,10 @@ class CUDT int minSndSize(int len = 0) const { + const int ps = maxPayloadSize(); if (len == 0) // wierd, can't use non-static data member as default argument! - len = m_iMaxSRTPayloadSize; - return m_bMessageAPI ? (len+m_iMaxSRTPayloadSize-1)/m_iMaxSRTPayloadSize : 1; + len = ps; + return m_config.m_bMessageAPI ? (len+ps-1)/ps : 1; } int32_t makeTS(const time_point& from_time) const @@ -487,11 +416,13 @@ class CUDT CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop std::set& pollset() { return m_sPollID; } + CSrtConfig m_config; + SRTU_PROPERTY_RO(SRTSOCKET, id, m_SocketID); SRTU_PROPERTY_RO(bool, isClosing, m_bClosing); SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); - SRTU_PROPERTY_RO(bool, isSynReceiving, m_bSynRecving); + SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.m_bSynRecving); SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); @@ -717,7 +648,7 @@ class CUDT int64_t withOverhead(int64_t basebw) { - return (basebw * (100 + m_iOverheadBW))/100; + return (basebw * (100 + m_config.m_iOverheadBW))/100; } static double Bps2Mbps(int64_t basebw) @@ -738,12 +669,12 @@ class CUDT int sndSpaceLeft() { - return sndBuffersLeft() * m_iMaxSRTPayloadSize; + return sndBuffersLeft() * maxPayloadSize(); } int sndBuffersLeft() { - return m_iSndBufSize - m_pSndBuffer->getCurrBufSize(); + return m_config.m_iSndBufSize - m_pSndBuffer->getCurrBufSize(); } time_point socketStartTime() @@ -766,84 +697,30 @@ class CUDT SRTSOCKET m_SocketID; // UDT socket number SRTSOCKET m_PeerID; // peer id, for multiplexer - int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes - size_t m_zOPT_ExpPayloadSize; // Expected average payload size (user option) - - // Options - int m_iMSS; // Maximum Segment Size, in bytes - bool m_bSynSending; // Sending syncronization mode - bool m_bSynRecving; // Receiving syncronization mode - int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side - int m_iSndBufSize; // Maximum UDT sender buffer size - int m_iRcvBufSize; // Maximum UDT receiver buffer size - linger m_Linger; // Linger information on close - int m_iUDPSndBufSize; // UDP sending buffer size - int m_iUDPRcvBufSize; // UDP receiving buffer size - bool m_bRendezvous; // Rendezvous connection mode - - duration m_tdConnTimeOut; // connect timeout in milliseconds - bool m_bDriftTracer; - int m_iSndTimeOut; // sending timeout in milliseconds - int m_iRcvTimeOut; // receiving timeout in milliseconds - bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer - int64_t m_llMaxBW; // maximum data transfer rate (threshold) - int m_iIpTTL; - int m_iIpToS; -#ifdef SRT_ENABLE_BINDTODEVICE - std::string m_BindToDevice; -#endif - // These fields keep the options for encryption - // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is - // created later and takes values from these. - HaiCrypt_Secret m_CryptoSecret; - int m_iSndCryptoKeyLen; - - // XXX Consider removing. The m_bDataSender stays here - // in order to maintain the HS side selection in HSv4. - bool m_bDataSender; - // HSv4 (legacy handshake) support) time_point m_tsSndHsLastTime; //Last SRT handshake request time int m_iSndHsRetryCnt; //SRT handshake retries left - bool m_bMessageAPI; - bool m_bOPT_TsbPd; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem) - int m_iOPT_TsbPdDelay; // Agent's Rx latency - int m_iOPT_PeerTsbPdDelay; // Peer's Rx latency for the traffic made by Agent's Tx. - bool m_bOPT_TLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx. - int m_iOPT_SndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off - bool m_bOPT_StrictEncryption; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected. - int m_OPT_GroupConnect; - std::string m_sStreamName; - int m_iOPT_PeerIdleTimeout; // Timeout for hearing anything from the peer. - uint32_t m_uOPT_StabilityTimeout; - int m_iOPT_RetransmitAlgo; - - int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds - int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds - bool m_bTLPktDrop; // Enable Too-late Packet Drop - int64_t m_llInputBW; // Input stream rate (bytes/sec) - // 0: use internally estimated input bandwidth - int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0) - bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports - int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) #if ENABLE_EXPERIMENTAL_BONDING SRT_GROUP_TYPE m_HSGroupType; // group type about-to-be-set in the handshake #endif private: - UniquePtr m_pCryptoControl; // congestion control SRT class (small data extension) - CCache* m_pCache; // network information cache + int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes + int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds + int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds + bool m_bTLPktDrop; // Enable Too-late Packet Drop + UniquePtr m_pCryptoControl; // congestion control SRT class (small data extension) + CCache* m_pCache; // network information cache // Congestion control std::vector m_Slots[TEV_E_SIZE]; - SrtCongestion m_CongCtl; + SrtCongestion m_CongCtl; // Packet filtering PacketFilter m_PacketFilter; - std::string m_OPT_PktFilterConfigString; SRT_ARQLevel m_PktFilterRexmitLevel; - std::string m_sPeerPktFilterConfigString; + std::string m_sPeerPktFilterConfigString; // Attached tool function void EmitSignal(ETransmissionEvent tev, EventVariant var); @@ -969,7 +846,6 @@ class CUDT CRcvLossList* m_pRcvLossList; //< Receiver loss list std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. int m_iReorderTolerance; //< Current value of dynamic reorder tolerance - int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_uKmPreAnnouncePkt; - m_KmRefreshRatePkt = m_parent->m_uKmRefreshRatePkt; + m_KmPreAnnouncePkt = m_parent->m_config.m_uKmPreAnnouncePkt; + m_KmRefreshRatePkt = m_parent->m_config.m_uKmRefreshRatePkt; if ( side == HSD_INITIATOR ) { diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index e6116cbb5..bdd57e789 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -21,6 +21,7 @@ queue.cpp congctl.cpp srt_c_api.cpp window.cpp +socketconfig.cpp srt_compat.c strerror_defs.cpp sync.cpp @@ -64,6 +65,7 @@ packet.h sync.h queue.h congctl.h +socketconfig.h srt_compat.h threadname.h utilities.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c4fe2e488..9faee8126 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -290,7 +290,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_iBusy() , m_iSndOldestMsgNo(SRT_MSGNO_NONE) , m_iSndAckedMsgNo(SRT_MSGNO_NONE) - , m_uOPT_StabilityTimeout(CUDT::COMM_DEF_STABILITY_TIMEOUT_US) + , m_uOPT_StabilityTimeout(CSrtConfig::COMM_DEF_STABILITY_TIMEOUT_US) // -1 = "undefined"; will become defined with first added socket , m_iMaxPayloadSize(-1) , m_bSynRecving(true) @@ -422,7 +422,7 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) const int val = cast_optval(optval, optlen); // Search if you already have SRTO_PEERIDLETIMEO set - int idletmo = CUDT::COMM_RESPONSE_TIMEOUT_MS; + int idletmo = CSrtConfig::COMM_RESPONSE_TIMEOUT_MS; vector::iterator f = find_if(m_config.begin(), m_config.end(), ConfigItem::OfType(SRTO_PEERIDLETIMEO)); if (f != m_config.end()) @@ -525,16 +525,16 @@ void CUDTGroup::deriveSettings(CUDT* u) // the option is altered on the group. // SRTO_RCVSYN - m_bSynRecving = u->m_bSynRecving; + m_bSynRecving = u->m_config.m_bSynRecving; // SRTO_SNDSYN - m_bSynSending = u->m_bSynSending; + m_bSynSending = u->m_config.m_bSynSending; // SRTO_RCVTIMEO - m_iRcvTimeOut = u->m_iRcvTimeOut; + m_iRcvTimeOut = u->m_config.m_iRcvTimeOut; // SRTO_SNDTIMEO - m_iSndTimeOut = u->m_iSndTimeOut; + m_iSndTimeOut = u->m_config.m_iSndTimeOut; // Ok, this really is disgusting, but there's only one way // to properly do it. Would be nice to have some more universal @@ -547,14 +547,15 @@ void CUDTGroup::deriveSettings(CUDT* u) // to be potentially replicated on the socket. So both pre // and post options apply. -#define IM(option, field) importOption(m_config, option, u->field) +#define IM(option, field) importOption(m_config, option, u->m_config.field) +#define IMF(option, field) importOption(m_config, option, u->field) IM(SRTO_MSS, m_iMSS); IM(SRTO_FC, m_iFlightFlagSize); // Nonstandard - importOption(m_config, SRTO_SNDBUF, u->m_iSndBufSize * (u->m_iMSS - CPacket::UDP_HDR_SIZE)); - importOption(m_config, SRTO_RCVBUF, u->m_iRcvBufSize * (u->m_iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_SNDBUF, u->m_config.m_iSndBufSize * (u->m_config.m_iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_RCVBUF, u->m_config.m_iRcvBufSize * (u->m_config.m_iMSS - CPacket::UDP_HDR_SIZE)); IM(SRTO_LINGER, m_Linger); IM(SRTO_UDP_SNDBUF, m_iUDPSndBufSize); @@ -569,29 +570,32 @@ void CUDTGroup::deriveSettings(CUDT* u) IM(SRTO_OHEADBW, m_iOverheadBW); IM(SRTO_IPTOS, m_iIpToS); IM(SRTO_IPTTL, m_iIpTTL); - IM(SRTO_TSBPDMODE, m_bOPT_TsbPd); - IM(SRTO_RCVLATENCY, m_iOPT_TsbPdDelay); - IM(SRTO_PEERLATENCY, m_iOPT_PeerTsbPdDelay); - IM(SRTO_SNDDROPDELAY, m_iOPT_SndDropDelay); - IM(SRTO_PAYLOADSIZE, m_zOPT_ExpPayloadSize); - IM(SRTO_TLPKTDROP, m_bTLPktDrop); - IM(SRTO_STREAMID, m_sStreamName); + IM(SRTO_TSBPDMODE, m_bTSBPD); + IM(SRTO_RCVLATENCY, m_iRcvLatency); + IM(SRTO_PEERLATENCY, m_iPeerLatency); + IM(SRTO_SNDDROPDELAY, m_iSndDropDelay); + IM(SRTO_PAYLOADSIZE, m_zExpPayloadSize); + IMF(SRTO_TLPKTDROP, m_bTLPktDrop); + + importOption(m_config, SRTO_STREAMID, u->m_config.m_StreamName.str()); + IM(SRTO_MESSAGEAPI, m_bMessageAPI); IM(SRTO_NAKREPORT, m_bRcvNakReport); IM(SRTO_MINVERSION, m_lMinimumPeerSrtVersion); - IM(SRTO_ENFORCEDENCRYPTION, m_bOPT_StrictEncryption); + IM(SRTO_ENFORCEDENCRYPTION, m_bEnforcedEnc); IM(SRTO_IPV6ONLY, m_iIpV6Only); - IM(SRTO_PEERIDLETIMEO, m_iOPT_PeerIdleTimeout); - IM(SRTO_GROUPSTABTIMEO, m_uOPT_StabilityTimeout); - IM(SRTO_PACKETFILTER, m_OPT_PktFilterConfigString); + IM(SRTO_PEERIDLETIMEO, m_iPeerIdleTimeout); + IM(SRTO_GROUPSTABTIMEO, m_uStabilityTimeout); + + importOption(m_config, SRTO_PACKETFILTER, u->m_config.m_PacketFilterConfig.str()); importOption(m_config, SRTO_PBKEYLEN, u->m_pCryptoControl->KeyLen()); // Passphrase is empty by default. Decipher the passphrase and // store as passphrase option - if (u->m_CryptoSecret.len) + if (u->m_config.m_CryptoSecret.len) { - string password((const char*)u->m_CryptoSecret.str, u->m_CryptoSecret.len); + string password((const char*)u->m_config.m_CryptoSecret.str, u->m_config.m_CryptoSecret.len); m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), password.size())); } @@ -609,6 +613,7 @@ void CUDTGroup::deriveSettings(CUDT* u) // are assigned to configurable items. #undef IM +#undef IMF } bool CUDTGroup::applyFlags(uint32_t flags, HandshakeSide hsd) @@ -674,7 +679,7 @@ inline int fillValue(void* optval, int len, V value) static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) { - static const linger def_linger = {1, CUDT::DEF_LINGER_S}; + static const linger def_linger = {1, CSrtConfig::DEF_LINGER_S}; switch (optname) { default: @@ -692,7 +697,7 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) RD(16); case SRTO_MSS: - RD(CUDT::DEF_MSS); + RD(CSrtConfig::DEF_MSS); case SRTO_SNDSYN: RD(true); @@ -701,18 +706,18 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) case SRTO_ISN: RD(SRT_SEQNO_NONE); case SRTO_FC: - RD(CUDT::DEF_FLIGHT_SIZE); + RD(CSrtConfig::DEF_FLIGHT_SIZE); case SRTO_SNDBUF: case SRTO_RCVBUF: - w_optlen = fillValue((pw_optval), w_optlen, CUDT::DEF_BUFFER_SIZE * (CUDT::DEF_MSS - CPacket::UDP_HDR_SIZE)); + w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::UDP_HDR_SIZE)); break; case SRTO_LINGER: RD(def_linger); case SRTO_UDP_SNDBUF: case SRTO_UDP_RCVBUF: - RD(CUDT::DEF_UDP_BUFFER_SIZE); + RD(CSrtConfig::DEF_UDP_BUFFER_SIZE); case SRTO_RENDEZVOUS: RD(false); case SRTO_SNDTIMEO: diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 121a717d1..25fef4165 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -246,7 +246,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); init.payload_size = parent->OPT_PayloadSize(); - init.rcvbuf_size = parent->m_iRcvBufSize; + init.rcvbuf_size = parent->m_config.m_iRcvBufSize; // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8daa31ea4..1437d4a1c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1024,7 +1024,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // This queue is used only in case of Async mode (rendezvous or caller-listener). // Synchronous connection requests are handled in startConnect() completely. - if (!i->m_pUDT->m_bSynRecving) + if (!i->m_pUDT->m_config.m_bSynRecving) { IF_HEAVY_LOGGING(++debug_nupd); @@ -1536,7 +1536,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // asynchronous connect: call connect here // otherwise wait for the UDT socket to retrieve this packet - if (!u->m_bSynRecving) + if (!u->m_config.m_bSynRecving) { HLOGC(cnlog.Debug, log << "AsyncOrRND: packet RESOLVED TO @" << id << " -- continuing as ASYNC CONNECT"); // This is practically same as processConnectResponse, just this applies diff --git a/srtcore/queue.h b/srtcore/queue.h index 0ff8bb205..6a3ba65f6 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -57,6 +57,7 @@ modified by #include "channel.h" #include "common.h" #include "packet.h" +#include "socketconfig.h" #include "netinet_any.h" #include "utilities.h" #include @@ -539,15 +540,9 @@ struct CMultiplexer int m_iPort; // The UDP port number of this multiplexer int m_iIPversion; // Address family (AF_INET or AF_INET6) - int m_iIpTTL; - int m_iIpToS; -#ifdef SRT_ENABLE_BINDTODEVICE - std::string m_BindToDevice; -#endif - int m_iMSS; // Maximum Segment Size int m_iRefCount; // number of UDT instances that are associated with this multiplexer - int m_iIpV6Only; // IPV6_V6ONLY option - bool m_bReusable; // if this one can be shared with others + + CSrtMuxerConfig m_mcfg; int m_iID; // multiplexer ID diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp new file mode 100644 index 000000000..ace07c1e2 --- /dev/null +++ b/srtcore/socketconfig.cpp @@ -0,0 +1,197 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Haivision Systems Inc. +*****************************************************************************/ +#include + +#include "srt.h" +#include "socketconfig.h" + +extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); + +static struct SrtConfigSetter +{ + setter_function* fn[SRTO_E_SIZE]; + + SrtConfigSetter() + { + memset(fn, 0, sizeof fn); + +#define DISPATCH(optname) fn[optname] = &CSrtConfigSetter::set; + + DISPATCH(SRTO_MSS); + DISPATCH(SRTO_FC); + DISPATCH(SRTO_SNDBUF); + DISPATCH(SRTO_RCVBUF); + DISPATCH(SRTO_LINGER); + DISPATCH(SRTO_UDP_SNDBUF); + DISPATCH(SRTO_UDP_RCVBUF); + DISPATCH(SRTO_RENDEZVOUS); + DISPATCH(SRTO_SNDTIMEO); + DISPATCH(SRTO_RCVTIMEO); + DISPATCH(SRTO_SNDSYN); + DISPATCH(SRTO_RCVSYN); + DISPATCH(SRTO_REUSEADDR); + DISPATCH(SRTO_MAXBW); + DISPATCH(SRTO_IPTTL); + DISPATCH(SRTO_IPTOS); + DISPATCH(SRTO_BINDTODEVICE); + DISPATCH(SRTO_INPUTBW); + DISPATCH(SRTO_OHEADBW); + DISPATCH(SRTO_SENDER); + DISPATCH(SRTO_TSBPDMODE); + DISPATCH(SRTO_LATENCY); + DISPATCH(SRTO_RCVLATENCY); + DISPATCH(SRTO_PEERLATENCY); + DISPATCH(SRTO_TLPKTDROP); + DISPATCH(SRTO_SNDDROPDELAY); + DISPATCH(SRTO_PASSPHRASE); + DISPATCH(SRTO_PBKEYLEN); + DISPATCH(SRTO_NAKREPORT); + DISPATCH(SRTO_CONNTIMEO); + DISPATCH(SRTO_DRIFTTRACER); + DISPATCH(SRTO_LOSSMAXTTL); + DISPATCH(SRTO_VERSION); + DISPATCH(SRTO_MINVERSION); + DISPATCH(SRTO_STREAMID); + DISPATCH(SRTO_CONGESTION); + DISPATCH(SRTO_MESSAGEAPI); + DISPATCH(SRTO_PAYLOADSIZE); + DISPATCH(SRTO_TRANSTYPE); +#if ENABLE_EXPERIMENTAL_BONDING + DISPATCH(SRTO_GROUPCONNECT); +#endif + DISPATCH(SRTO_KMREFRESHRATE); + DISPATCH(SRTO_KMPREANNOUNCE); + DISPATCH(SRTO_ENFORCEDENCRYPTION); + DISPATCH(SRTO_PEERIDLETIMEO); + DISPATCH(SRTO_IPV6ONLY); + DISPATCH(SRTO_PACKETFILTER); +#if ENABLE_EXPERIMENTAL_BONDING + DISPATCH(SRTO_GROUPSTABTIMEO); +#endif + DISPATCH(SRTO_RETRANSMITALGO); + +#undef DISPATCH + } +} srt_config_setter; + +int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) +{ + setter_function* fn = srt_config_setter.fn[optName]; + if (!fn) + return -1; // No such option + + fn(*this, optval, optlen); // MAY THROW EXCEPTION. + return 0; +} + +#if ENABLE_EXPERIMENTAL_BONDING +bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t optlen) +{ + // Check first if this option is allowed to be set + // as on a member socket. + + switch (optname) + { + case SRTO_BINDTODEVICE: + case SRTO_CONNTIMEO: + case SRTO_DRIFTTRACER: + //SRTO_FC - not allowed to be different among group members + case SRTO_GROUPSTABTIMEO: + //SRTO_INPUTBW - per transmission setting + case SRTO_IPTOS: + case SRTO_IPTTL: + case SRTO_KMREFRESHRATE: + case SRTO_KMPREANNOUNCE: + //SRTO_LATENCY - per transmission setting + //SRTO_LINGER - not for managed sockets + case SRTO_LOSSMAXTTL: + //SRTO_MAXBW - per transmission setting + //SRTO_MESSAGEAPI - groups are live mode only + //SRTO_MINVERSION - per group connection setting + case SRTO_NAKREPORT: + //SRTO_OHEADBW - per transmission setting + //SRTO_PACKETFILTER - per transmission setting + //SRTO_PASSPHRASE - per group connection setting + //SRTO_PASSPHRASE - per transmission setting + //SRTO_PBKEYLEN - per group connection setting + case SRTO_PEERIDLETIMEO: + case SRTO_RCVBUF: + //SRTO_RCVSYN - must be always false in groups + //SRTO_RCVTIMEO - must be alwyas -1 in groups + case SRTO_SNDBUF: + case SRTO_SNDDROPDELAY: + //SRTO_TLPKTDROP - per transmission setting + //SRTO_TSBPDMODE - per transmission setting + case SRTO_UDP_RCVBUF: + case SRTO_UDP_SNDBUF: + break; + + default: + // Other options are not allowed + return false; + } + + // Header size will get the size likely aligned, but it won't + // hurt if the memory size will be up to 4 bytes more than + // needed - and it's better to not risk that alighment rules + // will make these calculations result in less space than needed. + const size_t headersize = sizeof(SingleOption); + const size_t payload = std::min(sizeof(uint32_t), optlen); + unsigned char* mem = new unsigned char[headersize + payload]; + SingleOption* option = reinterpret_cast(mem); + option->option = optname; + option->length = (uint16_t) optlen; + memcpy(option->storage, optval, optlen); + + options.push_back(option); + + return true; +} +#endif diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h new file mode 100644 index 000000000..eaff48dec --- /dev/null +++ b/srtcore/socketconfig.h @@ -0,0 +1,1137 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef INC_SRT_SOCKETCONFIG_H +#define INC_SRT_SOCKETCONFIG_H + +#include "platform_sys.h" +#ifdef SRT_ENABLE_BINDTODEVICE +#include +#endif +#include +#include "haicrypt.h" +#include "congctl.h" +#include "packet.h" +#include "handshake.h" +#include "logger_defs.h" + +// SRT Version constants +#define SRT_VERSION_UNK 0 +#define SRT_VERSION_MAJ1 0x010000 /* Version 1 major */ +#define SRT_VERSION_MAJ(v) (0xFF0000 & (v)) /* Major number ensuring backward compatibility */ +#define SRT_VERSION_MIN(v) (0x00FF00 & (v)) +#define SRT_VERSION_PCH(v) (0x0000FF & (v)) + +// NOTE: SRT_VERSION is primarily defined in the build file. +extern const int32_t SRT_DEF_VERSION; + +struct CSrtMuxerConfig +{ + static const int DEF_UDP_BUFFER_SIZE = 65536; + + int m_iIpTTL; + int m_iIpToS; + int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) + bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer + +#ifdef SRT_ENABLE_BINDTODEVICE + std::string m_BindToDevice; +#endif + int m_iUDPSndBufSize; // UDP sending buffer size + int m_iUDPRcvBufSize; // UDP receiving buffer size + + // Some shortcuts +#define DEFINEP(psym) \ + char* pv##psym() { return (char*)&m_i##psym; } \ + const char* pv##psym() const { return (char*)&m_i##psym; } + + DEFINEP(UDPSndBufSize); + DEFINEP(UDPRcvBufSize); + DEFINEP(IpV6Only); + DEFINEP(IpTTL); + DEFINEP(IpToS); +#undef DEFINEP + + bool operator==(const CSrtMuxerConfig& other) const + { +#define CEQUAL(field) (field == other.field) + return CEQUAL(m_iIpTTL) + && CEQUAL(m_iIpToS) + && CEQUAL(m_iIpV6Only) + && CEQUAL(m_bReuseAddr) +#ifdef SRT_ENABLE_BINDTODEVICE + && CEQUAL(m_BindToDevice) +#endif + && CEQUAL(m_iUDPSndBufSize) + && CEQUAL(m_iUDPRcvBufSize); +#undef CEQUAL + } + + CSrtMuxerConfig() + : m_iIpTTL(-1) /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ + , m_iIpToS(-1) /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ + , m_iIpV6Only(-1) + , m_bReuseAddr(true) // This is default in SRT + , m_iUDPSndBufSize(DEF_UDP_BUFFER_SIZE) + , m_iUDPRcvBufSize(DEF_UDP_BUFFER_SIZE) + { + } +}; + +struct CSrtConfig; + +typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); + +template +struct CSrtConfigSetter +{ + static setter_function set; +}; + +template +class StringStorage +{ + char stor[SIZE + 1]; + uint16_t len; + + // NOTE: default copying allowed. + +public: + StringStorage() + : len(0) + { + memset(stor, 0, sizeof stor); + } + + bool set(const char* s, size_t length) + { + if (length >= SIZE) + return false; + + memcpy(stor, s, length); + stor[length] = 0; + len = length; + return true; + } + + bool set(const std::string& s) + { + if (s.size() >= SIZE) + return false; + + s.copy(stor, SIZE); + stor[SIZE] = 0; + len = s.size(); + return true; + } + + std::string str() const + { + return len == 0 ? std::string() : std::string(stor); + } + + const char* c_str() const + { + return stor; + } + + size_t size() const { return size_t(len); } + bool empty() const { return len == 0; } +}; + +struct CSrtConfig: CSrtMuxerConfig +{ + typedef srt::sync::steady_clock::time_point time_point; + typedef srt::sync::steady_clock::duration duration; + + static const int + DEF_MSS = 1500, + DEF_FLIGHT_SIZE = 25600, + DEF_BUFFER_SIZE = 8192, //Rcv buffer MUST NOT be bigger than Flight Flag size + DEF_LINGER_S = 3*60, // 3 minutes + DEF_CONNTIMEO_S = 3; // 3 seconds + + static const int COMM_RESPONSE_TIMEOUT_MS = 5 * 1000; // 5 seconds + static const uint32_t COMM_DEF_STABILITY_TIMEOUT_US = 80 * 1000; + + // Mimimum recv flight flag size is 32 packets + static const int DEF_MAX_FLIGHT_PKT = 32; + static const size_t MAX_SID_LENGTH = 512; + static const size_t MAX_PFILTER_LENGTH = 64; + static const size_t MAX_CONG_LENGTH = 16; + + int m_iMSS; // Maximum Segment Size, in bytes + size_t m_zExpPayloadSize; // Expected average payload size (user option) + + // Options + bool m_bSynSending; // Sending syncronization mode + bool m_bSynRecving; // Receiving syncronization mode + int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side + int m_iSndBufSize; // Maximum UDT sender buffer size + int m_iRcvBufSize; // Maximum UDT receiver buffer size + linger m_Linger; // Linger information on close + bool m_bRendezvous; // Rendezvous connection mode + + duration m_tdConnTimeOut; // connect timeout in milliseconds + bool m_bDriftTracer; + int m_iSndTimeOut; // sending timeout in milliseconds + int m_iRcvTimeOut; // receiving timeout in milliseconds + int64_t m_llMaxBW; // maximum data transfer rate (threshold) + + // These fields keep the options for encryption + // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is + // created later and takes values from these. + HaiCrypt_Secret m_CryptoSecret; + int m_iSndCryptoKeyLen; + + // XXX Consider removing. The m_bDataSender stays here + // in order to maintain the HS side selection in HSv4. + bool m_bDataSender; + + bool m_bMessageAPI; + bool m_bTSBPD; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem) + int m_iRcvLatency; // Agent's Rx latency + int m_iPeerLatency; // Peer's Rx latency for the traffic made by Agent's Tx. + bool m_bTLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx. + int m_iSndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off + bool m_bEnforcedEnc; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected. + int m_GroupConnect; + int m_iPeerIdleTimeout; // Timeout for hearing anything from the peer. + uint32_t m_uStabilityTimeout; + int m_iRetransmitAlgo; + + int64_t m_llInputBW; // Input stream rate (bytes/sec) + // 0: use internally estimated input bandwidth + int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0) + bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports + int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance + + // For the use of CCryptoControl + // HaiCrypt configuration + unsigned int m_uKmRefreshRatePkt; + unsigned int m_uKmPreAnnouncePkt; + + uint32_t m_lSrtVersion; + uint32_t m_lMinimumPeerSrtVersion; + + StringStorage m_Congestion; + StringStorage m_PacketFilterConfig; + StringStorage m_StreamName; + + // Shortcuts and utilities + int32_t flightCapacity() + { + return std::min(m_iRcvBufSize, m_iFlightFlagSize); + } + + CSrtConfig() + : m_iMSS(DEF_MSS) + , m_zExpPayloadSize(SRT_LIVE_DEF_PLSIZE) + , m_bSynSending(true) + , m_bSynRecving(true) + , m_iFlightFlagSize(DEF_FLIGHT_SIZE) + , m_iSndBufSize(DEF_BUFFER_SIZE) + , m_iRcvBufSize(DEF_BUFFER_SIZE) + , m_bRendezvous(false) + , m_tdConnTimeOut(srt::sync::seconds_from(DEF_CONNTIMEO_S)) + , m_bDriftTracer(true) + , m_iSndTimeOut(-1) + , m_iRcvTimeOut(-1) + , m_llMaxBW(-1) + , m_bDataSender(false) + , m_bMessageAPI(true) + , m_bTSBPD(true) + , m_iRcvLatency(SRT_LIVE_DEF_LATENCY_MS) + , m_iPeerLatency(0) + , m_bTLPktDrop(true) + , m_iSndDropDelay(0) + , m_bEnforcedEnc(true) + , m_GroupConnect(0) + , m_iPeerIdleTimeout(COMM_RESPONSE_TIMEOUT_MS) + , m_uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) + , m_iRetransmitAlgo(0) + , m_llInputBW(0) + , m_iOverheadBW(25) + , m_bRcvNakReport(true) + , m_iMaxReorderTolerance(0) // Sensible optimal value is 10, 0 preserves old behavior + , m_uKmRefreshRatePkt(0) + , m_uKmPreAnnouncePkt(0) + , m_lSrtVersion(SRT_DEF_VERSION) + , m_lMinimumPeerSrtVersion(SRT_VERSION_MAJ1) + + { + // Default UDT configurations + m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; + + // Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option + // for other modes. + m_Linger.l_onoff = 0; + m_Linger.l_linger = 0; + m_CryptoSecret.len = 0; + m_iSndCryptoKeyLen = 0; + + // Default congestion is "live". + // Available builtin congestions: "file". + // Others can be registerred. + m_Congestion.set("live", 4); + } + + ~CSrtConfig() + { + // Wipeout critical data + memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); + } + + int set(SRT_SOCKOPT optName, const void* val, int size); + + // Could be later made a more robust version with + // dispatching to the right data type. + template + void set(const void* val, int size) + { + CSrtConfigSetter::set(*this, val, size); + } +}; + + +#if ENABLE_EXPERIMENTAL_BONDING + +struct SRT_SocketOptionObject +{ + struct SingleOption + { + uint16_t option; + uint16_t length; + unsigned char storage[1]; // NOTE: Variable length object! + }; + + + std::vector options; + + SRT_SocketOptionObject() {} + + ~SRT_SocketOptionObject() + { + for (size_t i = 0; i < options.size(); ++i) + { + // Convert back + unsigned char* mem = reinterpret_cast(options[i]); + delete[] mem; + } + } + + bool add(SRT_SOCKOPT optname, const void* optval, size_t optlen); +}; +#endif + +template +inline T cast_optval(const void* optval) +{ + return *reinterpret_cast(optval); +} + +template +inline T cast_optval(const void* optval, int optlen) +{ + if (optlen > 0 && optlen != sizeof(T)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + return cast_optval(optval); +} + +// This function is to make it possible for both C and C++ +// API to accept both bool and int types for boolean options. +// (it's not that C couldn't use , it's that people +// often forget to use correct type). +template <> +inline bool cast_optval(const void* optval, int optlen) +{ + if (optlen == sizeof(bool)) + { + return *reinterpret_cast(optval); + } + + if (optlen == sizeof(int)) + { + // 0!= is a windows warning-killer int-to-bool conversion + return 0 != *reinterpret_cast(optval); + } + return false; +} + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int ival = cast_optval(optval, optlen); + if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.m_iMSS = ival; + + // Packet size cannot be greater than UDP buffer size + if (co.m_iMSS > co.m_iUDPSndBufSize) + co.m_iMSS = co.m_iUDPSndBufSize; + if (co.m_iMSS > co.m_iUDPRcvBufSize) + co.m_iMSS = co.m_iUDPRcvBufSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int fc = cast_optval(optval, optlen); + if (fc < 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + + co.m_iFlightFlagSize = std::min(fc, +co.DEF_MAX_FLIGHT_PKT); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int bs = cast_optval(optval, optlen); + if (bs <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.m_iSndBufSize = bs / (co.m_iMSS - CPacket::UDP_HDR_SIZE); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Mimimum recv buffer size is 32 packets + const int mssin_size = co.m_iMSS - CPacket::UDP_HDR_SIZE; + + // XXX This magic 32 deserves some constant + if (val > mssin_size * co.DEF_MAX_FLIGHT_PKT) + co.m_iRcvBufSize = val / mssin_size; + else + co.m_iRcvBufSize = co.DEF_MAX_FLIGHT_PKT; + + // recv buffer MUST not be greater than FC size + if (co.m_iRcvBufSize > co.m_iFlightFlagSize) + co.m_iRcvBufSize = co.m_iFlightFlagSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_Linger = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iUDPSndBufSize = std::max(co.m_iMSS, cast_optval(optval, optlen)); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iUDPRcvBufSize = std::max(co.m_iMSS, cast_optval(optval, optlen)); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bRendezvous = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iSndTimeOut = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iRcvTimeOut = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bSynSending = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bSynRecving = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bReuseAddr = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_llMaxBW = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int val = cast_optval(optval, optlen); + if (!(val == -1) && !((val >= 1) && (val <= 255))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.m_iIpTTL = cast_optval(optval); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iIpToS = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_BINDTODEVICE + using namespace std; + using namespace srt_logging; + + string val; + if (optlen == -1) + val = (const char *)optval; + else + val.assign((const char *)optval, optlen); + if (val.size() >= IFNAMSIZ) + { + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.m_BindToDevice = val; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_llInputBW = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int32_t val = cast_optval(optval, optlen); + if (val < 5 || val > 100) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.m_iOverheadBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bDataSender = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bTSBPD = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iRcvLatency = cast_optval(optval, optlen); + co.m_iPeerLatency = cast_optval(optval); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iRcvLatency = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iPeerLatency = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bTLPktDrop = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + // Surprise: you may be connected to alter this option. + // The application may manipulate this option on sender while transmitting. + co.m_iSndDropDelay = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + // Password must be 10-80 characters. + // Or it can be empty to clear the password. + if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + memset(&co.m_CryptoSecret, 0, sizeof(co.m_CryptoSecret)); + co.m_CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; + co.m_CryptoSecret.len = (optlen <= (int)sizeof(co.m_CryptoSecret.str) ? optlen : (int)sizeof(co.m_CryptoSecret.str)); + memcpy((co.m_CryptoSecret.str), optval, co.m_CryptoSecret.len); +#else + (void)co; // prevent warning + (void)optval; + if (optlen == 0) + return; // Allow to set empty passphrase if no encryption supported. + + LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + const int v = cast_optval(optval, optlen); + int const allowed[4] = { + 0, // Default value, if this results for initiator, defaults to 16. See below. + 16, // AES-128 + 24, // AES-192 + 32 // AES-256 + }; + const int *const allowed_end = allowed + 4; + if (std::find(allowed, allowed_end, v) == allowed_end) + { + LOGC(aclog.Error, + log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Note: This works a little different in HSv4 and HSv5. + + // HSv4: + // The party that is set SRTO_SENDER will send KMREQ, and it will + // use default value 16, if SRTO_PBKEYLEN is the default value 0. + // The responder that receives KMRSP has nothing to say about + // PBKEYLEN anyway and it will take the length of the key from + // the initiator (sender) as a good deal. + // + // HSv5: + // The initiator (independently on the sender) will send KMREQ, + // and as it should be the sender to decide about the PBKEYLEN. + // Your application should do the following then: + // 1. The sender should set PBKEYLEN to the required value. + // 2. If the sender is initiator, it will create the key using + // its preset PBKEYLEN (or default 16, if not set) and the + // receiver-responder will take it as a good deal. + // 3. Leave the PBKEYLEN value on the receiver as default 0. + // 4. If sender is responder, it should then advertise the PBKEYLEN + // value in the initial handshake messages (URQ_INDUCTION if + // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case + // of rendezvous, as it is the matter of luck who of them will + // eventually become the initiator). This way the receiver + // being an initiator will set m_iSndCryptoKeyLen before setting + // up KMREQ for sending to the sender-responder. + // + // Note that in HSv5 if both sides set PBKEYLEN, the responder + // wins, unless the initiator is a sender (the effective PBKEYLEN + // will be the one advertised by the responder). If none sets, + // PBKEYLEN will default to 16. + + co.m_iSndCryptoKeyLen = v; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bRcvNakReport = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt::sync; + co.m_tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bDriftTracer = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iMaxReorderTolerance = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_lSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_lMinimumPeerSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.m_StreamName.set((const char*)optval, optlen); + } +}; + +template<> +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); + + // Translate alias + if (val == "vod") + val = "file"; + + bool res = SrtCongestion::exists(val); + if (!res) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.m_Congestion.set(val); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bMessageAPI = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + if (*(int *)optval > SRT_LIVE_MAX_PLSIZE) + { + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (!co.m_PacketFilterConfig.empty()) + { + // This means that the filter might have been installed before, + // and the fix to the maximum payload size was already applied. + // This needs to be checked now. + SrtFilterConfig fc; + if (!ParseFilterConfig(co.m_PacketFilterConfig.str(), fc)) + { + // Break silently. This should not happen + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (co.m_zExpPayloadSize > efc_max_payload_size) + { + LOGC(aclog.Error, + log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size + << " required for packet filter header"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + co.m_zExpPayloadSize = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + // XXX Note that here the configuration for SRTT_LIVE + // is the same as DEFAULT VALUES for these fields set + // in CUDT::CUDT. + switch (cast_optval(optval, optlen)) + { + case SRTT_LIVE: + // Default live options: + // - tsbpd: on + // - latency: 120ms + // - linger: off + // - congctl: live + // - extraction method: message (reading call extracts one message) + co.m_bTSBPD = true; + co.m_iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; + co.m_iPeerLatency = 0; + co.m_bTLPktDrop = true; + co.m_iSndDropDelay = 0; + co.m_bMessageAPI = true; + co.m_bRcvNakReport = true; + co.m_zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + co.m_Linger.l_onoff = 0; + co.m_Linger.l_linger = 0; + co.m_Congestion.set("live", 4); + break; + + case SRTT_FILE: + // File transfer mode: + // - tsbpd: off + // - latency: 0 + // - linger: 2 minutes (180s) + // - congctl: file (original UDT congestion control) + // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) + co.m_bTSBPD = false; + co.m_iRcvLatency = 0; + co.m_iPeerLatency = 0; + co.m_bTLPktDrop = false; + co.m_iSndDropDelay = -1; + co.m_bMessageAPI = false; + co.m_bRcvNakReport = false; + co.m_zExpPayloadSize = 0; // use maximum + co.m_Linger.l_onoff = 1; + co.m_Linger.l_linger = CSrtConfig::DEF_LINGER_S; + co.m_Congestion.set("file", 4); + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_GroupConnect = cast_optval(optval, optlen); + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + // If you first change the KMREFRESHRATE, KMPREANNOUNCE + // will be set to the maximum allowed value + co.m_uKmRefreshRatePkt = cast_optval(optval, optlen); + if (co.m_uKmPreAnnouncePkt == 0 || co.m_uKmPreAnnouncePkt > (co.m_uKmRefreshRatePkt - 1) / 2) + { + co.m_uKmPreAnnouncePkt = (co.m_uKmRefreshRatePkt - 1) / 2; + LOGC(aclog.Warn, + log << "SRTO_KMREFRESHRATE=0x" << std::hex << co.m_uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" + << std::hex << co.m_uKmPreAnnouncePkt); + } + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + const int val = cast_optval(optval, optlen); + const int kmref = co.m_uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.m_uKmRefreshRatePkt; + if (val > (kmref - 1) / 2) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=0x" << std::hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + << " - OPTION REJECTED."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.m_uKmPreAnnouncePkt = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_bEnforcedEnc = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iPeerIdleTimeout = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iIpV6Only = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + std::string arg((const char*)optval, optlen); + // Parse the configuration string prematurely + SrtFilterConfig fc; + if (!ParseFilterConfig(arg, fc)) + { + LOGC(aclog.Error, + log << "SRTO_FILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " + "FILTERTYPE (" + << fc.type << ") must be installed (or builtin)"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (co.m_zExpPayloadSize > efc_max_payload_size) + { + LOGC(aclog.Warn, + log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + << efc_max_payload_size << " bytes"); + co.m_zExpPayloadSize = efc_max_payload_size; + } + + co.m_PacketFilterConfig.set(arg); + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + // This option is meaningless for the socket itself. + // It's set here just for the sake of setting it on a listener + // socket so that it is then applied on the group when a + // group connection is configuired. + const int val = cast_optval(optval, optlen); + + // Search if you already have SRTO_PEERIDLETIMEO set + + const int idletmo = co.m_iPeerIdleTimeout; + + // Both are in milliseconds. + // This option is RECORDED in microseconds, while + // idletmo is recorded in milliseconds, only translated to + // microseconds directly before use. + if (val >= idletmo) + { + LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val + << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.m_uStabilityTimeout = val * 1000; + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.m_iRetransmitAlgo = cast_optval(optval, optlen); + } +}; + +#if TEMPLATE +#endif + +#endif diff --git a/srtcore/srt.h b/srtcore/srt.h index 639fbc1a3..db392470a 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -246,7 +246,9 @@ typedef enum SRT_SOCKOPT { SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake #endif SRTO_PACKETFILTER = 60, // Add and configure a packet filter - SRTO_RETRANSMITALGO = 61 // An option to select packet retransmission algorithm + SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm + + SRTO_E_SIZE // Always last element, not a valid option. } SRT_SOCKOPT; diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index dd3d9828c..e2200f226 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -34,7 +34,7 @@ class TestFECRebuilding: public testing::Test isn - 1, // It's passed in this form to PacketFilter constructor, it should increase it isn - 1, // XXX Probably this better be changed. plsize, - CUDT::DEF_BUFFER_SIZE + CSrtConfig::DEF_BUFFER_SIZE }; From 1c7da112cd978a223fa52b053e0305406587372a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 18 Feb 2021 11:44:44 +0100 Subject: [PATCH 068/790] [core] Removed invalid max socket ID check --- srtcore/handshake.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index ff3a30ebd..95f9cb899 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -192,8 +192,7 @@ bool CHandShake::valid() if (m_iVersion < CUDT::HS_VERSION_UDT4 || m_iISN < 0 || m_iISN >= CSeqNo::m_iMaxSeqNo || m_iMSS < 32 - || m_iFlightFlagSize < 2 - || m_iID >= CUDTUnited::MAX_SOCKET_VAL) + || m_iFlightFlagSize < 2) return false; return true; From 21058d5019ce46465e34148281976a5325670009 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 18 Feb 2021 17:13:21 +0100 Subject: [PATCH 069/790] [core] Fixed post-action for socket options --- srtcore/core.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0c9540d6d..fa21a3dcb 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -329,32 +329,26 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) } // Post-action, if applicable - if (IsSet(oflags, SRTO_POST_SPEC)) + if (IsSet(oflags, SRTO_POST_SPEC) && m_bConnected) { - if (m_bConnected) - if (m_bOpened) - throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - + switch (optName) { - switch (optName) - { - case SRTO_MAXBW: - updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET)); - break; + case SRTO_MAXBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET)); + break; - case SRTO_INPUTBW: - updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW)); - break; + case SRTO_INPUTBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW)); + break; - case SRTO_OHEADBW: - updateCC(TEV_INIT, EventVariant(TEV_INIT_OHEADBW)); - break; + case SRTO_OHEADBW: + updateCC(TEV_INIT, EventVariant(TEV_INIT_OHEADBW)); + break; - case SRTO_LOSSMAXTTL: - m_iReorderTolerance = m_config.m_iMaxReorderTolerance; + case SRTO_LOSSMAXTTL: + m_iReorderTolerance = m_config.m_iMaxReorderTolerance; - default: break; - } + default: break; } } } From 030b0d4f791313229fbd80ff5d4199d87afe7d18 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 19 Feb 2021 14:48:56 +0100 Subject: [PATCH 070/790] [core] Fixed wrong max socket ID value formula (#1816) --- srtcore/api.cpp | 10 +++++++--- srtcore/api.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5689cd1f4..480c78c63 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -205,7 +205,11 @@ m_ClosedSockets() const double rand1_0 = double(rand())/RAND_MAX; - m_SocketIDGenerator = 1 + int(MAX_SOCKET_VAL * rand1_0); + // Motivation: in case when rand() returns the value equal to RAND_MAX, + // rand1_0 == 1, so the below formula will be + // 1 + (MAX_SOCKET_VAL-1) * 1 = 1 + MAX_SOCKET_VAL - 1 = MAX_SOCKET_VAL + // which is the highest allowed value for the socket. + m_SocketIDGenerator = 1 + int((MAX_SOCKET_VAL-1) * rand1_0); m_SocketIDGenerator_init = m_SocketIDGenerator; // XXX An unlikely exception thrown from the below calls @@ -338,7 +342,7 @@ SRTSOCKET CUDTUnited::generateSocketID(bool for_group) { // We have a rollover on the socket value, so // definitely we haven't made the Columbus mistake yet. - m_SocketIDGenerator = MAX_SOCKET_VAL-1; + m_SocketIDGenerator = MAX_SOCKET_VAL; } // Check all sockets if any of them has this value. @@ -384,7 +388,7 @@ SRTSOCKET CUDTUnited::generateSocketID(bool for_group) // The socket value is in use. --sockval; if (sockval <= 0) - sockval = MAX_SOCKET_VAL-1; + sockval = MAX_SOCKET_VAL; // Before continuing, check if we haven't rolled back to start again // This is virtually impossible, so just make an RTI error. diff --git a/srtcore/api.h b/srtcore/api.h index 39ed9ce51..478b0955c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -203,7 +203,7 @@ friend class CRendezvousQueue; ~CUDTUnited(); // Public constants - static const int32_t MAX_SOCKET_VAL = 1 << 29; // maximum value for a regular socket + static const int32_t MAX_SOCKET_VAL = SRTGROUP_MASK - 1; // maximum value for a regular socket public: From 82159e4c06c95db5f58954a624d1ac52a9cd661f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Feb 2021 10:56:00 +0100 Subject: [PATCH 071/790] [core] Added SRTO_MININPUTBW socket option --- srtcore/core.cpp | 26 ++++++++--- srtcore/core.h | 2 +- srtcore/group.cpp | 3 +- srtcore/socketconfig.cpp | 97 ++++++++++++++++++++-------------------- srtcore/socketconfig.h | 29 +++++++++--- srtcore/srt.h | 1 + 6 files changed, 97 insertions(+), 61 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fa21a3dcb..a2198e9d8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -225,6 +225,7 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_RCVTIMEO, SRTO_MAXBW, SRTO_INPUTBW, + SRTO_MININPUTBW, SRTO_OHEADBW, SRTO_SNDDROPDELAY, SRTO_CONNTIMEO, @@ -263,6 +264,7 @@ struct SrtOptionAction flags[SRTO_TSBPDMODE] = SRTO_R_PRE; flags[SRTO_LATENCY] = SRTO_R_PRE; flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_MININPUTBW] = 0 | SRTO_POST_SPEC; flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; flags[SRTO_PASSPHRASE] = SRTO_R_PRE; flags[SRTO_PBKEYLEN] = SRTO_R_PRE; @@ -305,7 +307,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Restriction check - int oflags = srt_options_action.flags[optName]; + const int oflags = srt_options_action.flags[optName]; ScopedLock cg (m_ConnectionLock); ScopedLock sendguard (m_SendLock); @@ -321,7 +323,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); // Option execution. If this returns -1, there's no such option. - int status = m_config.set(optName, optval, optlen); + const int status = m_config.set(optName, optval, optlen); if (status == -1) { LOGC(aclog.Error, log << CONID() << "OPTION: #" << optName << " UNKNOWN"); @@ -338,6 +340,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_INPUTBW: + case SRTO_MININPUTBW: updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW)); break; @@ -433,15 +436,26 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_MAXBW: + if (optlen < sizeof(m_config.m_llMaxBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); *(int64_t *)optval = m_config.m_llMaxBW; optlen = sizeof(int64_t); break; case SRTO_INPUTBW: + if (optlen < sizeof(m_config.m_llInputBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); *(int64_t*)optval = m_config.m_llInputBW; - optlen = sizeof(int64_t); + optlen = sizeof(int64_t); break; + case SRTO_MININPUTBW: + if (optlen < sizeof (m_config.m_llMinInputBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + *(int64_t*)optval = m_config.m_llMinInputBW; + optlen = sizeof(int64_t); + break; + case SRTO_OHEADBW: *(int32_t *)optval = m_config.m_iOverheadBW; optlen = sizeof(int32_t); @@ -7085,7 +7099,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) } else { - // No need to calculate input reate if the bandwidth is set + // No need to calculate input rate if the bandwidth is set const bool disable_in_rate_calc = (bw != 0); m_pSndBuffer->resetInputRateSmpPeriod(disable_in_rate_calc); } @@ -7116,8 +7130,8 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) * and sendrate skyrockets for retransmission. * Keep previously set maximum in that case (inputbw == 0). */ - if (inputbw != 0) - m_CongCtl->updateBandwidth(0, withOverhead(inputbw)); // Bytes/sec + if (inputbw >= 0) + m_CongCtl->updateBandwidth(0, withOverhead(std::max(m_config.m_llMinInputBW, inputbw))); // Bytes/sec } } diff --git a/srtcore/core.h b/srtcore/core.h index 6ed5d02e0..ac489eac0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -116,7 +116,7 @@ enum AckDataItem }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); -static const size_t SRT_SOCKOPT_NPOST = 12; +static const size_t SRT_SOCKOPT_NPOST = 13; extern const SRT_SOCKOPT srt_post_opt_list []; enum GroupDataItem diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 9faee8126..a967b146b 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -465,7 +465,7 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) { // There's at least one socket in the group, so only // post-options are allowed. - if (!std::binary_search(srt_post_opt_list, srt_post_opt_list + SRT_SOCKOPT_NPOST, optName)) + if (!binary_search(srt_post_opt_list, srt_post_opt_list + SRT_SOCKOPT_NPOST, optName)) { LOGC(gmlog.Error, log << "setsockopt(group): Group is connected, this option can't be altered"); throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); @@ -567,6 +567,7 @@ void CUDTGroup::deriveSettings(CUDT* u) // Reuseaddr: true by default and should only be true. IM(SRTO_MAXBW, m_llMaxBW); IM(SRTO_INPUTBW, m_llInputBW); + IM(SRTO_MININPUTBW, m_llMinInputBW); IM(SRTO_OHEADBW, m_iOverheadBW); IM(SRTO_IPTOS, m_iIpToS); IM(SRTO_IPTTL, m_iIpTTL); diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index ace07c1e2..e50bb08f5 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -64,58 +64,59 @@ static struct SrtConfigSetter #define DISPATCH(optname) fn[optname] = &CSrtConfigSetter::set; - DISPATCH(SRTO_MSS); - DISPATCH(SRTO_FC); - DISPATCH(SRTO_SNDBUF); - DISPATCH(SRTO_RCVBUF); - DISPATCH(SRTO_LINGER); - DISPATCH(SRTO_UDP_SNDBUF); - DISPATCH(SRTO_UDP_RCVBUF); - DISPATCH(SRTO_RENDEZVOUS); - DISPATCH(SRTO_SNDTIMEO); - DISPATCH(SRTO_RCVTIMEO); - DISPATCH(SRTO_SNDSYN); - DISPATCH(SRTO_RCVSYN); - DISPATCH(SRTO_REUSEADDR); - DISPATCH(SRTO_MAXBW); - DISPATCH(SRTO_IPTTL); - DISPATCH(SRTO_IPTOS); - DISPATCH(SRTO_BINDTODEVICE); - DISPATCH(SRTO_INPUTBW); - DISPATCH(SRTO_OHEADBW); - DISPATCH(SRTO_SENDER); - DISPATCH(SRTO_TSBPDMODE); - DISPATCH(SRTO_LATENCY); - DISPATCH(SRTO_RCVLATENCY); - DISPATCH(SRTO_PEERLATENCY); - DISPATCH(SRTO_TLPKTDROP); - DISPATCH(SRTO_SNDDROPDELAY); - DISPATCH(SRTO_PASSPHRASE); - DISPATCH(SRTO_PBKEYLEN); - DISPATCH(SRTO_NAKREPORT); - DISPATCH(SRTO_CONNTIMEO); - DISPATCH(SRTO_DRIFTTRACER); - DISPATCH(SRTO_LOSSMAXTTL); - DISPATCH(SRTO_VERSION); - DISPATCH(SRTO_MINVERSION); - DISPATCH(SRTO_STREAMID); - DISPATCH(SRTO_CONGESTION); - DISPATCH(SRTO_MESSAGEAPI); - DISPATCH(SRTO_PAYLOADSIZE); - DISPATCH(SRTO_TRANSTYPE); + DISPATCH(SRTO_MSS); + DISPATCH(SRTO_FC); + DISPATCH(SRTO_SNDBUF); + DISPATCH(SRTO_RCVBUF); + DISPATCH(SRTO_LINGER); + DISPATCH(SRTO_UDP_SNDBUF); + DISPATCH(SRTO_UDP_RCVBUF); + DISPATCH(SRTO_RENDEZVOUS); + DISPATCH(SRTO_SNDTIMEO); + DISPATCH(SRTO_RCVTIMEO); + DISPATCH(SRTO_SNDSYN); + DISPATCH(SRTO_RCVSYN); + DISPATCH(SRTO_REUSEADDR); + DISPATCH(SRTO_MAXBW); + DISPATCH(SRTO_IPTTL); + DISPATCH(SRTO_IPTOS); + DISPATCH(SRTO_BINDTODEVICE); + DISPATCH(SRTO_INPUTBW); + DISPATCH(SRTO_MININPUTBW); + DISPATCH(SRTO_OHEADBW); + DISPATCH(SRTO_SENDER); + DISPATCH(SRTO_TSBPDMODE); + DISPATCH(SRTO_LATENCY); + DISPATCH(SRTO_RCVLATENCY); + DISPATCH(SRTO_PEERLATENCY); + DISPATCH(SRTO_TLPKTDROP); + DISPATCH(SRTO_SNDDROPDELAY); + DISPATCH(SRTO_PASSPHRASE); + DISPATCH(SRTO_PBKEYLEN); + DISPATCH(SRTO_NAKREPORT); + DISPATCH(SRTO_CONNTIMEO); + DISPATCH(SRTO_DRIFTTRACER); + DISPATCH(SRTO_LOSSMAXTTL); + DISPATCH(SRTO_VERSION); + DISPATCH(SRTO_MINVERSION); + DISPATCH(SRTO_STREAMID); + DISPATCH(SRTO_CONGESTION); + DISPATCH(SRTO_MESSAGEAPI); + DISPATCH(SRTO_PAYLOADSIZE); + DISPATCH(SRTO_TRANSTYPE); #if ENABLE_EXPERIMENTAL_BONDING - DISPATCH(SRTO_GROUPCONNECT); + DISPATCH(SRTO_GROUPCONNECT); #endif - DISPATCH(SRTO_KMREFRESHRATE); - DISPATCH(SRTO_KMPREANNOUNCE); - DISPATCH(SRTO_ENFORCEDENCRYPTION); - DISPATCH(SRTO_PEERIDLETIMEO); - DISPATCH(SRTO_IPV6ONLY); - DISPATCH(SRTO_PACKETFILTER); + DISPATCH(SRTO_KMREFRESHRATE); + DISPATCH(SRTO_KMPREANNOUNCE); + DISPATCH(SRTO_ENFORCEDENCRYPTION); + DISPATCH(SRTO_PEERIDLETIMEO); + DISPATCH(SRTO_IPV6ONLY); + DISPATCH(SRTO_PACKETFILTER); #if ENABLE_EXPERIMENTAL_BONDING - DISPATCH(SRTO_GROUPSTABTIMEO); + DISPATCH(SRTO_GROUPSTABTIMEO); #endif - DISPATCH(SRTO_RETRANSMITALGO); + DISPATCH(SRTO_RETRANSMITALGO); #undef DISPATCH } diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index eaff48dec..c415d6ae2 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -247,8 +247,8 @@ struct CSrtConfig: CSrtMuxerConfig uint32_t m_uStabilityTimeout; int m_iRetransmitAlgo; - int64_t m_llInputBW; // Input stream rate (bytes/sec) - // 0: use internally estimated input bandwidth + int64_t m_llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth + int64_t m_llMinInputBW; // Minimum input stream rate estimate (bytes/sec) int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0) bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance @@ -298,6 +298,7 @@ struct CSrtConfig: CSrtMuxerConfig , m_uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) , m_iRetransmitAlgo(0) , m_llInputBW(0) + , m_llMinInputBW(0) , m_iOverheadBW(25) , m_bRcvNakReport(true) , m_iMaxReorderTolerance(0) // Sensible optimal value is 10, 0 preserves old behavior @@ -559,7 +560,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_llMaxBW = cast_optval(optval, optlen); + const int64_t val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.m_llMaxBW = val; } }; @@ -620,7 +625,21 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_llInputBW = cast_optval(optval, optlen); + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.m_llInputBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.m_llMinInputBW = val; } }; template<> @@ -628,7 +647,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - int32_t val = cast_optval(optval, optlen); + const int32_t val = cast_optval(optval, optlen); if (val < 5 || val > 100) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.m_iOverheadBW = val; diff --git a/srtcore/srt.h b/srtcore/srt.h index db392470a..f9fa24655 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -222,6 +222,7 @@ typedef enum SRT_SOCKOPT { SRTO_PEERVERSION, // Peer SRT Version (from SRT Handshake) SRTO_CONNTIMEO = 36, // Connect timeout in msec. Caller default: 3000, rendezvous (x 10) SRTO_DRIFTTRACER = 37, // Enable or disable drift tracer + SRTO_MININPUTBW = 38, // Minimum estimate of input stream rate. // (some space left) SRTO_SNDKMSTATE = 40, // (GET) the current state of the encryption at the peer side SRTO_RCVKMSTATE, // (GET) the current state of the encryption at the agent side From 4aa91000b612708ab00b629cfc065bec48177d33 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Feb 2021 10:56:33 +0100 Subject: [PATCH 072/790] [apps] Added mininputbw URI query parameter --- apps/socketoptions.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 6f6305b4c..6e26b3520 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -224,6 +224,7 @@ const SocketOption srt_options [] { { "ipttl", 0, SRTO_IPTTL, SocketOption::PRE, SocketOption::INT, nullptr}, { "iptos", 0, SRTO_IPTOS, SocketOption::PRE, SocketOption::INT, nullptr}, { "inputbw", 0, SRTO_INPUTBW, SocketOption::POST, SocketOption::INT64, nullptr}, + { "mininputbw", 0, SRTO_MININPUTBW, SocketOption::POST, SocketOption::INT64, nullptr}, { "oheadbw", 0, SRTO_OHEADBW, SocketOption::POST, SocketOption::INT, nullptr}, { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr}, From f7ee434dd70045c5d43737d8e2aa3b352722852b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Feb 2021 10:56:51 +0100 Subject: [PATCH 073/790] [tests] Added tests for SRTO_MININPUTBW --- test/test_socket_options.cpp | 173 ++++++++++++++++++++++++++++++----- 1 file changed, 149 insertions(+), 24 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 705db5a09..a94d18eec 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -33,6 +33,36 @@ class TestSocketOptions // cleanup any pending stuff, but no exceptions allowed } +public: + void StartListener() + { + // Specify address of the listener + sockaddr* psa = (sockaddr*)&m_sa; + ASSERT_NE(srt_bind(m_listen_sock, psa, sizeof m_sa), SRT_ERROR); + + srt_listen(m_listen_sock, 1); + } + + SRTSOCKET EstablishConnection() + { + auto accept_async = [](SRTSOCKET listen_sock) { + sockaddr_in client_address; + int length = sizeof(sockaddr_in); + const SRTSOCKET accepted_socket = srt_accept(listen_sock, (sockaddr*)&client_address, &length); + return accepted_socket; + }; + auto accept_res = async(launch::async, accept_async, m_listen_sock); + + sockaddr* psa = (sockaddr*)&m_sa; + const int connect_res = srt_connect(m_caller_sock, psa, sizeof m_sa); + EXPECT_EQ(connect_res, SRT_SUCCESS); + + const SRTSOCKET accepted_sock = accept_res.get(); + EXPECT_NE(accepted_sock, SRT_INVALID_SOCK); + + return accepted_sock; + } + protected: // SetUp() is run immediately before a test starts. void SetUp() @@ -40,6 +70,11 @@ class TestSocketOptions ASSERT_GE(srt_startup(), 0); const int yes = 1; + memset(&m_sa, 0, sizeof m_sa); + m_sa.sin_family = AF_INET; + m_sa.sin_port = htons(5200); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1); + m_caller_sock = srt_create_socket(); ASSERT_NE(m_caller_sock, SRT_INVALID_SOCK); ASSERT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_SUCCESS); // for async connect @@ -63,6 +98,7 @@ class TestSocketOptions protected: // put in any custom data members that you need + sockaddr_in m_sa; SRTSOCKET m_caller_sock = SRT_INVALID_SOCK; SRTSOCKET m_listen_sock = SRT_INVALID_SOCK; @@ -78,30 +114,8 @@ TEST_F(TestSocketOptions, LossMaxTTL) const int loss_max_ttl = 5; ASSERT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_LOSSMAXTTL, &loss_max_ttl, sizeof loss_max_ttl), SRT_SUCCESS); - // Specify address - sockaddr_in sa; - memset(&sa, 0, sizeof sa); - sa.sin_family = AF_INET; - sa.sin_port = htons(5200); - ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); - sockaddr* psa = (sockaddr*)&sa; - ASSERT_NE(srt_bind(m_listen_sock, psa, sizeof sa), SRT_ERROR); - - srt_listen(m_listen_sock, 1); - - auto accept_async = [](SRTSOCKET listen_sock) { - sockaddr_in client_address; - int length = sizeof(sockaddr_in); - const SRTSOCKET accepted_socket = srt_accept(listen_sock, (sockaddr*)&client_address, &length); - return accepted_socket; - }; - auto accept_res = async(launch::async, accept_async, m_listen_sock); - - const int connect_res = srt_connect(m_caller_sock, psa, sizeof sa); - EXPECT_EQ(connect_res, SRT_SUCCESS); - - const SRTSOCKET accepted_sock = accept_res.get(); - ASSERT_NE(accepted_sock, SRT_INVALID_SOCK); + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); int opt_val = 0; int opt_len = 0; @@ -121,3 +135,114 @@ TEST_F(TestSocketOptions, LossMaxTTL) } +// Try to set/get SRTO_MININPUTBW with wrong optlen +TEST_F(TestSocketOptions, MinInputBWWrongLen) +{ + int64_t mininputbw = 0; + int optlen = (int)(sizeof mininputbw) - 1; + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + optlen += 2; + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS) << "Bigger storage is allowed"; + EXPECT_EQ(optlen, (int)(sizeof mininputbw)); + + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, sizeof mininputbw - 1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, sizeof mininputbw + 1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); +} + +// Check the default SRTO_MININPUTBW is SRT_PACING_MAXBW_DEFAULT +TEST_F(TestSocketOptions, MinInputBWDefault) +{ + const int mininputbw_expected = 0; + int64_t mininputbw = 1; + int optlen = (int)(sizeof mininputbw); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS); + EXPECT_EQ(optlen, (int)(sizeof mininputbw)); + EXPECT_EQ(mininputbw, mininputbw_expected); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check both listener and accepted socket have default values + for (SRTSOCKET sock : { m_listen_sock, accepted_sock }) + { + optlen = (int)(sizeof mininputbw); + EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS); + EXPECT_EQ(optlen, (int)(sizeof mininputbw)); + EXPECT_EQ(mininputbw, mininputbw_expected); + } + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +// Check setting and getting SRT_MININPUTBW +TEST_F(TestSocketOptions, MinInputBWSet) +{ + const int64_t mininputbw_dflt = 0; + const int64_t mininputbw = 50000000; + int optlen = (int)(sizeof mininputbw); + + int64_t bw = -100; + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_ERROR) << "Has to be a non-negative number"; + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, mininputbw_dflt); + + bw = mininputbw; + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, mininputbw); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (SRTSOCKET sock : { m_listen_sock, accepted_sock }) + { + optlen = (int)(sizeof bw); + EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(optlen, (int)(sizeof bw)); + EXPECT_EQ(bw, mininputbw); + } + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +// Check setting and getting SRTO_MININPUTBW in runtime +TEST_F(TestSocketOptions, MinInputBWRuntime) +{ + const int64_t mininputbw = 50000000; + + // Establish a connection + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Test a connected socket + int64_t bw = mininputbw; + int optlen = (int)(sizeof bw); + EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, mininputbw); + + bw = 0; + EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_INPUTBW, &bw, sizeof bw), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_INPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, 0); + + EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MAXBW, &bw, sizeof bw), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MAXBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, 0); + + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, mininputbw); + + const int64_t new_mininputbw = 20000000; + bw = new_mininputbw; + EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS); + EXPECT_EQ(bw, new_mininputbw); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + From 6012fdf483e8ddaca53a9f88e34ab5641c549b2a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Feb 2021 10:57:15 +0100 Subject: [PATCH 074/790] [docs] Added SRTO_MININPUTBW to the docs --- docs/APISocketOptions.md | 23 ++++++++++++++++++++--- docs/srt-live-transmit.md | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index aa34f4fb4..780b92228 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -211,6 +211,7 @@ The following table lists SRT socket options in alphabetical order. Option detai | [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | | [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | | [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | | [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | | [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | @@ -501,12 +502,13 @@ context than inside the listener callback handler, the value is undefined. | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | | `SRTO_INPUTBW` | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -This option is effective only if `SRTO_MAXBW` is set to 0 (relative). It -controls the maximum bandwidth together with `SRTO_OHEADBW` option according +This option is effective only if [`SRTO_MAXBW`](#SRTO_MAXBW) is set to 0 (relative). It +controls the maximum bandwidth together with [`SRTO_OHEADBW`](#SRTO_OHEADBW) option according to the formula: `MAXBW = INPUTBW * (100 + OHEADBW) / 100`. When this option is set to 0 (automatic) then the real INPUTBW value will be estimated from the rate of the input (cases when the application calls the `srt_send*` -function) during transmission. +function) during transmission. The minimum allowed estimate value is restricted +by [`SRTO_MININPUTBW`](#SRTO_MININPUTBW), meaning `INPUTBW = MAX(INPUTBW_ESTIMATE; MININPUTBW)`. *Recommended: set this option to the anticipated bitrate of your live stream and keep the default 25% value for `SRTO_OHEADBW`*. @@ -515,6 +517,21 @@ and keep the default 25% value for `SRTO_OHEADBW`*. --- +#### SRTO_MININPUTBW + +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| ----------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | +| `SRTO_MININPUTBW` | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | + +This option is effective only if both `SRTO_MAXBW` and `SRTO_INPUTBW` are set to 0. +It controls the minimum allowed value of the input butrate estimate. + +See [`SRTO_INPUTBW`](#SRTO_INPUTBW). + +[Return to list](#list-of-options) + +--- + #### SRTO_IPTOS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | diff --git a/docs/srt-live-transmit.md b/docs/srt-live-transmit.md index 0a2dfb02d..a124c631c 100644 --- a/docs/srt-live-transmit.md +++ b/docs/srt-live-transmit.md @@ -307,6 +307,7 @@ All other parameters are SRT socket options. The following have the following va | `linger` | 0.. | `SRTO_LINGER` | Link linger value | | `lossmaxttl` | 0.. | `SRTO_LOSSMAXTTL` | Packet reorder tolerance. | | `maxbw` | 0.. | `SRTO_MAXBW` | Bandwidth limit in bytes | +| `mininputbw` | 0.. | `SRTO_MININPUTBW` | Minimum allowed estimate of `SRTO_INPUTBW` | | `messageapi` | `bool` | `SRTO_MESSAGEAPI` | Enable SRT message mode. | | `minversion` | maj.min.rev | `SRTO_MINVERSION` | Minimum SRT library version of a peer. | | `mss` | 76.. | `SRTO_MSS` | MTU size | From 1dced8b6c2a6fc249d82dbc7a2cae176f252852f Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 26 Feb 2021 10:18:51 +0100 Subject: [PATCH 075/790] [build] Fixed possibility to compile examples with C++03 standard (#1830) --- CMakeLists.txt | 16 +++++++++------- docs/BuildOptions.md | 5 +++++ docs/DevelopersGuide.md | 30 +++++++++++++++++++++++++++--- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 626bbc7e8..d251f5712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,9 +415,6 @@ if (NOT ENABLE_CXX11) if (ENABLE_STDCXX_SYNC) message(FATAL_ERROR "ENABLE_STDCXX_SYNC is set, but C++11 is disabled by ENABLE_CXX11") endif() - if (DEFINED USE_CXX_STD) - message(FATAL_ERROR "USE_CXX_STD can be set only when ENABLE_CXX11 is on") - endif() elseif (ENABLE_STDCXX_SYNC) add_definitions(-DENABLE_STDCXX_SYNC=1) if (DEFINED USE_CXX_STD) @@ -461,9 +458,14 @@ if (DEFINED USE_CXX_STD) # Set this through independent flags set (USE_CXX_STD_LIB ${STDCXX}) - set (USE_CXX_STD_APP "") set (FORCE_CXX_STANDARD 1) - message(STATUS "C++ STANDARD: library: C++${STDCXX}, but apps still at least C++11") + if (NOT ENABLE_APPS) + set (USE_CXX_STD_APP ${STDCXX}) + message(STATUS "C++ STANDARD: library: C++${STDCXX}, apps disabled (examples will follow C++${STDCXX})") + else() + set (USE_CXX_STD_APP "") + message(STATUS "C++ STANDARD: library: C++${STDCXX}, but apps still at least C++11") + endif() elseif (FORCE_CXX_STANDARD_GNUONLY) # CMake is too old to handle CMAKE_CXX_STANDARD, # use bare GNU options. @@ -805,10 +807,10 @@ macro(srt_set_stdcxx targetname spec) if (NOT "${stdcxxspec}" STREQUAL "") if (FORCE_CXX_STANDARD_GNUONLY) target_compile_options(${targetname} PRIVATE -std=c++${stdcxxspec}) - #message(STATUS "C++ STD: ${targetname}: forced C++${stdcxxspec} standard - GNU option: -std=c++${stdcxxspec}") + message(STATUS "C++ STD: ${targetname}: forced C++${stdcxxspec} standard - GNU option: -std=c++${stdcxxspec}") else() set_target_properties(${targetname} PROPERTIES CXX_STANDARD ${stdcxxspec}) - #message(STATUS "C++ STD: ${targetname}: forced C++${stdcxxspec} standard - portable way") + message(STATUS "C++ STD: ${targetname}: forced C++${stdcxxspec} standard - portable way") endif() else() message(STATUS "APP: ${targetname}: using default C++ standard") diff --git a/docs/BuildOptions.md b/docs/BuildOptions.md index 2e1ec0b12..adf718031 100644 --- a/docs/BuildOptions.md +++ b/docs/BuildOptions.md @@ -303,7 +303,12 @@ when it is expected to be revived. **`--use-c++-std=`** +Enforce using particular C++ standard when compiling. When using this option +remember that: +* Allowed values are: 98, 03, 11, 14, 17 and 20 +* If you use 98/03 and `--enable-apps`, apps will be still using C++11 +* This option is only supported on GNU and Clang compilers (will be ignored on others) **`--use-gnustl`** diff --git a/docs/DevelopersGuide.md b/docs/DevelopersGuide.md index 3114f4f9e..cc021cb6c 100644 --- a/docs/DevelopersGuide.md +++ b/docs/DevelopersGuide.md @@ -77,12 +77,36 @@ Please see the following document for `configure` usage: [BuildOptions.md](./Bui The build output is in the `_build` directory. The following applications can be found there. -* `srt-live-transmit` ā€” A sample application to transmit a live stream from source medium (UDP/SRT/`stdin`) +* `srt-live-transmit` - A sample application to transmit a live stream from source medium (UDP/SRT/`stdin`) to the target medium (UDP/SRT/`stdout`). See [srt-live-transmit.md](./srt-live-transmit.md) for more info. -* `srt-file-transmit` ā€” A sample application to transmit files with SRT. -* `srt-tunnel` ā€” A sample application to set up an SRT tunnel for TCP traffic. See [srt-tunnel.md](./srt-tunnel.md) for more info. +* `srt-file-transmit` - A sample application to transmit files with SRT. +* `srt-tunnel` - A sample application to set up an SRT tunnel for TCP traffic. See [srt-tunnel.md](./srt-tunnel.md) for more info. * `tests-srt` - unit testing application. +## Language standard requirements + +The following conventions for the language standard are used in this project: + +1. The SRT library requires C++03 (also known as C++98) standard. +2. The examples (to be enabled in cmake by `-DENABLE_EXAMPLES=1`) require either C++03 or C89 standard. +3. The following require C++11 standard: + * demo applications + * testing applications (to be enabled in cmake by `-DENABLE_TESTING=1`) + * unit tests (to be enabled in cmake by `-DENABLE_UNITTESTS=1`) + +Note that C++11 standard will be enforced if you have enabled applications +and haven't specified the C++ standard explicitly. When you have an old compiler +that does not support C++11 and you want to compile as many parts as possible, +the simplest way is to use the following options (in cmake): + +``` +-DENABLE_APPS=0 -DUSE_CXX_STD=03 -DENABLE_EXAMPLES=1 +``` + +Note also that there are several other options that, when enabled, may require +that the SRT library be compiled using C++11 standard (`-DENABLE_STDCXX_SYNC=1` +for example). + ## Project Structure The SRT installation has the following folders: From e90b332a9e0b2e1aa17f74283f67f151205a2542 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Feb 2021 16:32:22 +0100 Subject: [PATCH 076/790] [core] Fixed missing pktSentUnique stats for backup --- srtcore/group.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index a967b146b..40e31fc56 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4154,6 +4154,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } + // At least one link has succeeded, update sending stats. + m_stats.sent.Update(len); + // Now fill in the socket table. Check if the size is enough, if not, // then set the pointer to NULL and set the correct size. From 54e435ee7da3087dacb3e12ea98753f140f864b9 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Feb 2021 16:50:49 +0100 Subject: [PATCH 077/790] [core] Fixed a missing group stats initialization --- srtcore/group.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 40e31fc56..854d3a4c3 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -317,6 +317,8 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) m_RcvEID = m_pGlobal->m_EPoll.create(&m_RcvEpolld); m_SndEID = m_pGlobal->m_EPoll.create(&m_SndEpolld); + m_stats.init(); + // Set this data immediately during creation before // two or more sockets start arguing about it. m_iLastSchedSeqNo = CUDT::generateISN(); From 93f710f37b7ea3c0fc6ae82a88712d3a5ad806df Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Feb 2021 17:22:57 +0100 Subject: [PATCH 078/790] [core] Fixed some int conversion build warnings --- srtcore/core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a2198e9d8..cef12c9ed 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -436,21 +436,21 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_MAXBW: - if (optlen < sizeof(m_config.m_llMaxBW)) + if (size_t(optlen) < sizeof(m_config.m_llMaxBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); *(int64_t *)optval = m_config.m_llMaxBW; optlen = sizeof(int64_t); break; case SRTO_INPUTBW: - if (optlen < sizeof(m_config.m_llInputBW)) + if (size_t(optlen) < sizeof(m_config.m_llInputBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); *(int64_t*)optval = m_config.m_llInputBW; optlen = sizeof(int64_t); break; case SRTO_MININPUTBW: - if (optlen < sizeof (m_config.m_llMinInputBW)) + if (size_t(optlen) < sizeof (m_config.m_llMinInputBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); *(int64_t*)optval = m_config.m_llMinInputBW; optlen = sizeof(int64_t); @@ -634,7 +634,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_CONNTIMEO: - *(int*)optval = count_milliseconds(m_config.m_tdConnTimeOut); + *(int*)optval = (int) count_milliseconds(m_config.m_tdConnTimeOut); optlen = sizeof(int); break; From 6254c1d390e57fd47f7a34a32b482d17fcd24a0e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Feb 2021 18:01:12 +0100 Subject: [PATCH 079/790] [tests] A basic check for initial group stats values --- test/test_bonding.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 5172fc756..7d02ee5bb 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -291,6 +291,20 @@ TEST(Bonding, CloseGroupAndSocket) } } + // Some basic checks for group stats + SRT_TRACEBSTATS stats; + EXPECT_EQ(srt_bstats(ss, &stats, true), SRT_SUCCESS); + EXPECT_EQ(stats.pktSent, 0); + EXPECT_EQ(stats.pktSentTotal, 0); + EXPECT_EQ(stats.pktSentUnique, 0); + EXPECT_EQ(stats.pktSentUniqueTotal, 0); + EXPECT_EQ(stats.pktRecv, 0); + EXPECT_EQ(stats.pktRecvTotal, 0); + EXPECT_EQ(stats.pktRecvUnique, 0); + EXPECT_EQ(stats.pktRecvUniqueTotal, 0); + EXPECT_EQ(stats.pktRcvDrop, 0); + EXPECT_EQ(stats.pktRcvDropTotal, 0); + std::cout << "Starting thread for sending:\n"; std::thread sender([ss] { char buf[1316]; From 0ee1b98aa4fedba5548010f23d18db6519c8258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 22 Feb 2021 16:48:50 +0100 Subject: [PATCH 080/790] [core][apps] Fixed restrictions for options and replication in the app array --- apps/socketoptions.hpp | 8 ++++---- srtcore/core.cpp | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 6e26b3520..2a14c3854 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -211,7 +211,7 @@ extern const std::map enummap_transtype; namespace { const SocketOption srt_options [] { { "transtype", 0, SRTO_TRANSTYPE, SocketOption::PRE, SocketOption::ENUM, &enummap_transtype }, - { "maxbw", 0, SRTO_MAXBW, SocketOption::PRE, SocketOption::INT64, nullptr}, + { "maxbw", 0, SRTO_MAXBW, SocketOption::POST, SocketOption::INT64, nullptr}, { "pbkeylen", 0, SRTO_PBKEYLEN, SocketOption::PRE, SocketOption::INT, nullptr}, { "passphrase", 0, SRTO_PASSPHRASE, SocketOption::PRE, SocketOption::STRING, nullptr}, @@ -229,11 +229,11 @@ const SocketOption srt_options [] { { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "tlpktdrop", 0, SRTO_TLPKTDROP, SocketOption::PRE, SocketOption::BOOL, nullptr}, - { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::POST, SocketOption::INT, nullptr}, + { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::PRE, SocketOption::INT, nullptr}, { "nakreport", 0, SRTO_NAKREPORT, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, { "drifttracer", 0, SRTO_DRIFTTRACER, SocketOption::POST, SocketOption::BOOL, nullptr}, - { "lossmaxttl", 0, SRTO_LOSSMAXTTL, SocketOption::PRE, SocketOption::INT, nullptr}, + { "lossmaxttl", 0, SRTO_LOSSMAXTTL, SocketOption::POST, SocketOption::INT, nullptr}, { "rcvlatency", 0, SRTO_RCVLATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "peerlatency", 0, SRTO_PEERLATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "minversion", 0, SRTO_MINVERSION, SocketOption::PRE, SocketOption::INT, nullptr}, @@ -254,7 +254,7 @@ const SocketOption srt_options [] { { "bindtodevice", 0, SRTO_BINDTODEVICE, SocketOption::PRE, SocketOption::STRING, nullptr}, #endif #if ENABLE_EXPERIMENTAL_BONDING - { "groupstabtimeo", 0, SRTO_GROUPSTABTIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, + { "groupstabtimeo", 0, SRTO_GROUPSTABTIMEO, SocketOption::POST, SocketOption::INT, nullptr}, #endif { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr } }; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cef12c9ed..10a827a5b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -288,6 +288,9 @@ struct SrtOptionAction flags[SRTO_ENFORCEDENCRYPTION] = SRTO_R_PRE; flags[SRTO_IPV6ONLY] = SRTO_R_PREBIND; flags[SRTO_PEERIDLETIMEO] = SRTO_R_PRE; +#ifdef SRT_ENABLE_BINDTODEVICE + flags[SRTO_BINDTODEVICE] = SRTO_R_PREBIND; +#endif #if ENABLE_EXPERIMENTAL_BONDING flags[SRTO_GROUPCONNECT] = SRTO_R_PRE; #endif From 6e32509398fdcb876d38019195eda9a466e71031 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 4 Mar 2021 11:41:49 +0100 Subject: [PATCH 081/790] [core] Fixed bug: wrong size setting for StringStorage. (#1841) With UT --- srtcore/socketconfig.h | 8 +----- test/test_utilities.cpp | 60 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index c415d6ae2..0c4936f93 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -163,13 +163,7 @@ class StringStorage bool set(const std::string& s) { - if (s.size() >= SIZE) - return false; - - s.copy(stor, SIZE); - stor[SIZE] = 0; - len = s.size(); - return true; + return set(s.c_str(), s.size()); } std::string str() const diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp index 42c761d63..7deabe736 100644 --- a/test/test_utilities.cpp +++ b/test/test_utilities.cpp @@ -211,3 +211,63 @@ TEST(CircularBuffer, Overall) IF_HEAVY_LOGGING(cerr << "DONE.\n"); } +TEST(ConfigString, Setting) +{ + using namespace std; + + static const auto STRSIZE = 20; + StringStorage s; + + EXPECT_TRUE(s.empty()); + EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.str(), std::string()); + + char example_ac1[] = "example_long"; + char example_ac2[] = "short"; + char example_ac3[] = "example_longer"; + char example_acx[] = "example_long_excessively"; + char example_ace[] = ""; + + // According to the standard, this array gets automatically + // the number of characters + 1 for terminating 0. Get sizeof()-1 + // to get the number of characters. + + EXPECT_TRUE(s.set(example_ac1, sizeof (example_ac1)-1)); + EXPECT_EQ(s.size(), sizeof (example_ac1)-1); + EXPECT_FALSE(s.empty()); + + EXPECT_TRUE(s.set(example_ac2, sizeof (example_ac2)-1)); + EXPECT_EQ(s.size(), sizeof (example_ac2)-1); + + EXPECT_TRUE(s.set(example_ac3, sizeof (example_ac3)-1)); + EXPECT_EQ(s.size(), sizeof (example_ac3)-1); + + EXPECT_FALSE(s.set(example_acx, sizeof (example_acx)-1)); + EXPECT_EQ(s.size(), sizeof (example_ac3)-1); + + EXPECT_TRUE(s.set(example_ace, sizeof (example_ace)-1)); + EXPECT_EQ(s.size(), 0); + + string example_s1 = "example_long"; + string example_s2 = "short"; + string example_s3 = "example_longer"; + string example_sx = "example_long_excessively"; + string example_se = ""; + + EXPECT_TRUE(s.set(example_s1)); + EXPECT_EQ(s.size(), example_s1.size()); + EXPECT_FALSE(s.empty()); + + EXPECT_TRUE(s.set(example_s2)); + EXPECT_EQ(s.size(), example_s2.size()); + + EXPECT_TRUE(s.set(example_s3)); + EXPECT_EQ(s.size(), example_s3.size()); + + EXPECT_FALSE(s.set(example_sx)); + EXPECT_EQ(s.size(), example_s3.size()); + + EXPECT_TRUE(s.set(example_se)); + EXPECT_EQ(s.size(), 0); + EXPECT_TRUE(s.empty()); +} From 12d03fe95ba40438e87651fc951d321526b58114 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 4 Mar 2021 13:44:44 +0100 Subject: [PATCH 082/790] [core] Removed 'm_' prefix in socket config structs (#1839) --- srtcore/api.cpp | 34 +-- srtcore/channel.cpp | 66 ++--- srtcore/congctl.h | 4 +- srtcore/core.cpp | 535 +++++++++++++++++++-------------------- srtcore/core.h | 22 +- srtcore/crypto.cpp | 4 +- srtcore/group.cpp | 74 +++--- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/socketconfig.h | 412 +++++++++++++++--------------- 10 files changed, 571 insertions(+), 586 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 480c78c63..5239693fc 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -168,7 +168,7 @@ bool CUDTSocket::readReady() bool CUDTSocket::writeReady() { return (m_pUDT->m_bConnected - && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.m_iSndBufSize)) + && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) || broken(); } @@ -531,7 +531,7 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, w_hs.m_iISN = ns->m_pUDT->m_iISN; w_hs.m_iMSS = ns->m_pUDT->MSS(); - w_hs.m_iFlightFlagSize = ns->m_pUDT->m_config.m_iFlightFlagSize; + w_hs.m_iFlightFlagSize = ns->m_pUDT->m_config.iFlightFlagSize; w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; @@ -1004,7 +1004,7 @@ int CUDTUnited::listen(const SRTSOCKET u, int backlog) // [[using assert(s->m_Status == OPENED)]]; // listen is not supported in rendezvous connection setup - if (s->m_pUDT->m_config.m_bRendezvous) + if (s->m_pUDT->m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); s->m_uiBackLog = backlog; @@ -1077,7 +1077,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); // no "accept" in rendezvous connection setup - if (ls->m_pUDT->m_config.m_bRendezvous) + if (ls->m_pUDT->m_config.bRendezvous) { LOGC(cnlog.Fatal, log << "CUDTUnited::accept: RENDEZVOUS flag passed through check in srt_listen when it set listen state"); // This problem should never happen because `srt_listen` function should have @@ -1107,7 +1107,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ ls->m_QueuedSockets.erase(b); accepted = true; } - else if (!ls->m_pUDT->m_config.m_bSynRecving) + else if (!ls->m_pUDT->m_config.bSynRecving) { accepted = true; } @@ -1122,7 +1122,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ if (u == CUDT::INVALID_SOCK) { // non-blocking receiving, no connection available - if (!ls->m_pUDT->m_config.m_bSynRecving) + if (!ls->m_pUDT->m_config.bSynRecving) throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); // listening socket is closed @@ -1134,13 +1134,13 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ throw CUDTException(MJ_SETUP, MN_CLOSED, 0); // Set properly the SRTO_GROUPCONNECT flag - s->core().m_config.m_GroupConnect = 0; + s->core().m_config.iGroupConnect = 0; // Check if LISTENER has the SRTO_GROUPCONNECT flag set, // and the already accepted socket has successfully joined // the mirror group. If so, RETURN THE GROUP ID, not the socket ID. #if ENABLE_EXPERIMENTAL_BONDING - if (ls->m_pUDT->m_config.m_GroupConnect == 1 && s->m_GroupOf) + if (ls->m_pUDT->m_config.iGroupConnect == 1 && s->m_GroupOf) { // Put a lock to protect the group against accidental deletion // in the meantime. @@ -1150,7 +1150,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ if (s->m_GroupOf) { u = s->m_GroupOf->m_GroupID; - s->core().m_config.m_GroupConnect = 1; // should be derived from ls, but make sure + s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure // Mark the beginning of the connection at the moment // when the group ID is returned to the app caller @@ -1498,14 +1498,14 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar } // Set it the groupconnect option, as all in-group sockets should have. - ns->m_pUDT->m_config.m_GroupConnect = 1; + ns->m_pUDT->m_config.iGroupConnect = 1; // Every group member will have always nonblocking // (this implies also non-blocking connect/accept). // The group facility functions will block when necessary // using epoll_wait. - ns->m_pUDT->m_config.m_bSynRecving = false; - ns->m_pUDT->m_config.m_bSynSending = false; + ns->m_pUDT->m_config.bSynRecving = false; + ns->m_pUDT->m_config.bSynSending = false; HLOGC(aclog.Debug, log << "groupConnect: NOTIFIED AS PENDING @" << sid << " both read and write"); // If this socket is not to block the current connect process, @@ -1827,7 +1827,7 @@ int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_ if (s->m_Status == SRTS_INIT) { - if (s->m_pUDT->m_config.m_bRendezvous) + if (s->m_pUDT->m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); // If bind() was done first on this socket, then the @@ -1957,7 +1957,7 @@ int CUDTUnited::close(CUDTSocket* s) HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); - const bool synch_close_snd = s->m_pUDT->m_config.m_bSynSending; + const bool synch_close_snd = s->m_pUDT->m_config.bSynSending; SRTSOCKET u = s->m_SocketID; @@ -2322,7 +2322,7 @@ int CUDTUnited::selectEx( { if (s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() - < s->m_pUDT->m_config.m_iSndBufSize)) + < s->m_pUDT->m_config.iSndBufSize)) { writefds->push_back(s->m_SocketID); ++ count; @@ -2827,7 +2827,7 @@ void CUDTUnited::updateMux( // In such a case rely exclusively on that very socket and // use it the way as it is configured, of course, create also // always a new multiplexer for that very socket. - if (!udpsock && s->m_pUDT->m_config.m_bReuseAddr) + if (!udpsock && s->m_pUDT->m_config.bReuseAddr) { const int port = addr.hport(); @@ -2993,7 +2993,7 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) if (!mux && fallback) { // It is allowed to reuse this multiplexer, but the socket must allow both IPv4 and IPv6 - if (fallback->m_mcfg.m_iIpV6Only == 0) + if (fallback->m_mcfg.iIpV6Only == 0) { HLOGC(smlog.Warn, log << "updateListenerMux: reusing multiplexer from different family"); mux = fallback; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index c6409ead8..c35510747 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -181,16 +181,16 @@ void CChannel::createSocket(int family) #endif #endif // ENABLE_SOCK_CLOEXEC - if ((m_mcfg.m_iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) + if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, - m_mcfg.pvIpV6Only(), sizeof(int)); + (const char*) &m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); if (res == -1) { int err = errno; char msg[160]; LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " - << m_mcfg.m_iIpV6Only << ": " << SysStrError(err, msg, 159)); + << m_mcfg.iIpV6Only << ": " << SysStrError(err, msg, 159)); } } @@ -266,22 +266,22 @@ void CChannel::setUDPSockOpt() #if defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value int maxsize = 64000; - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), sizeof(int))) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), sizeof(int))) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &maxsize, sizeof maxsize); + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &maxsize, sizeof maxsize); #else // for other systems, if requested is greated than maximum, the maximum value will be automactally used - if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), sizeof(int))) || - (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), sizeof(int)))) + if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) || + (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #endif - if (m_mcfg.m_iIpTTL != -1) + if (m_mcfg.iIpTTL != -1) { if (m_BindAddr.family() == AF_INET) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } else @@ -291,7 +291,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, m_mcfg.pvIpTTL(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -299,7 +299,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -307,11 +307,11 @@ void CChannel::setUDPSockOpt() } } - if (m_mcfg.m_iIpToS != -1) + if (m_mcfg.iIpToS != -1) { if (m_BindAddr.family() == AF_INET) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } else @@ -322,7 +322,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, m_mcfg.pvIpToS(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -332,7 +332,7 @@ void CChannel::setUDPSockOpt() // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), sizeof (int))) + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) { throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } @@ -341,7 +341,7 @@ void CChannel::setUDPSockOpt() } #ifdef SRT_ENABLE_BINDTODEVICE - if (!m_mcfg.m_BindToDevice.empty()) + if (!m_mcfg.sBindToDevice.empty()) { if (m_BindAddr.family() != AF_INET) { @@ -350,7 +350,7 @@ void CChannel::setUDPSockOpt() } if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, - m_mcfg.m_BindToDevice.c_str(), m_mcfg.m_BindToDevice.size())) + m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) { char buf[255]; const char* err = SysStrError(NET_ERROR, buf, 255); @@ -397,16 +397,16 @@ void CChannel::close() const int CChannel::getSndBufSize() { - socklen_t size = sizeof(socklen_t); - ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, m_mcfg.pvUDPSndBufSize(), &size); - return m_mcfg.m_iUDPSndBufSize; + socklen_t size = (socklen_t) sizeof m_mcfg.iUDPSndBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*) &m_mcfg.iUDPSndBufSize, &size); + return m_mcfg.iUDPSndBufSize; } int CChannel::getRcvBufSize() { - socklen_t size = sizeof(socklen_t); - ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, m_mcfg.pvUDPRcvBufSize(), &size); - return m_mcfg.m_iUDPRcvBufSize; + socklen_t size = (socklen_t) sizeof m_mcfg.iUDPRcvBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*) &m_mcfg.iUDPRcvBufSize, &size); + return m_mcfg.iUDPRcvBufSize; } void CChannel::setConfig(const CSrtMuxerConfig& config) @@ -419,14 +419,14 @@ int CChannel::getIpTTL() const if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - socklen_t size = sizeof(int); + socklen_t size = (socklen_t) sizeof m_mcfg.iIpTTL; if (m_BindAddr.family() == AF_INET) { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, m_mcfg.pvIpTTL(), &size); + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char*) &m_mcfg.iIpTTL, &size); } else if (m_BindAddr.family() == AF_INET6) { - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, m_mcfg.pvIpTTL(), &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char*) &m_mcfg.iIpTTL, &size); } else { @@ -434,7 +434,7 @@ int CChannel::getIpTTL() const LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - return m_mcfg.m_iIpTTL; + return m_mcfg.iIpTTL; } int CChannel::getIpToS() const @@ -442,15 +442,15 @@ int CChannel::getIpToS() const if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - socklen_t size = sizeof(int); + socklen_t size = (socklen_t) sizeof m_mcfg.iIpToS; if (m_BindAddr.family() == AF_INET) { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, m_mcfg.pvIpToS(), &size); + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char*) &m_mcfg.iIpToS, &size); } else if (m_BindAddr.family() == AF_INET6) { #ifdef IPV6_TCLASS - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, m_mcfg.pvIpToS(), &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char*) &m_mcfg.iIpToS, &size); #endif } else @@ -459,7 +459,7 @@ int CChannel::getIpToS() const LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - return m_mcfg.m_iIpToS; + return m_mcfg.iIpToS; } #ifdef SRT_ENABLE_BINDTODEVICE diff --git a/srtcore/congctl.h b/srtcore/congctl.h index bf6826c28..4f887f8be 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -188,8 +188,8 @@ class SrtCongestionControlBase virtual int ACKTimeout_us() const { return 0; } // Called when the settings concerning m_llMaxBW were changed. - // Arg 1: value of CUDT::m_llMaxBW - // Arg 2: value calculated out of CUDT::m_llInputBW and CUDT::m_iOverheadBW. + // Arg 1: value of CUDT's m_config.m_llMaxBW + // Arg 2: value calculated out of CUDT's m_config.llInputBW and m_config.iOverheadBW. virtual void updateBandwidth(int64_t, int64_t) {} virtual bool needsQuickACK(const CPacket&) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 10a827a5b..2d5cb2d86 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -144,18 +144,15 @@ void CUDT::construct() m_RejectReason = SRT_REJ_UNKNOWN; m_tsLastReqTime = steady_clock::time_point(); m_SrtHsSide = HSD_DRAW; - - m_lPeerSrtVersion = 0; // not defined until connected. - - m_iTsbPdDelay_ms = 0; - m_iPeerTsbPdDelay_ms = 0; - - m_bPeerTsbPd = false; - m_iPeerTsbPdDelay_ms = 0; - m_bTsbPd = false; - m_bTsbPdAckWakeup = false; - m_bGroupTsbPd = false; - m_bPeerTLPktDrop = false; + m_uPeerSrtVersion = 0; // not defined until connected. + m_iTsbPdDelay_ms = 0; + m_iPeerTsbPdDelay_ms = 0; + m_bPeerTsbPd = false; + m_iPeerTsbPdDelay_ms = 0; + m_bTsbPd = false; + m_bTsbPdAckWakeup = false; + m_bGroupTsbPd = false; + m_bPeerTLPktDrop = false; // Initilize mutex and condition variables initSynch(); @@ -197,7 +194,7 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) m_config = ancestor.m_config; m_SrtHsSide = ancestor.m_SrtHsSide; // actually it sets it to HSD_RESPONDER m_bTLPktDrop = ancestor.m_bTLPktDrop; - m_iReorderTolerance = m_config.m_iMaxReorderTolerance; // Initialize with maximum value + m_iReorderTolerance = m_config.iMaxReorderTolerance; // Initialize with maximum value // Runtime m_pCache = ancestor.m_pCache; @@ -352,7 +349,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; case SRTO_LOSSMAXTTL: - m_iReorderTolerance = m_config.m_iMaxReorderTolerance; + m_iReorderTolerance = m_config.iMaxReorderTolerance; default: break; } @@ -366,17 +363,17 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) switch (optName) { case SRTO_MSS: - *(int *)optval = m_config.m_iMSS; + *(int *)optval = m_config.iMSS; optlen = sizeof(int); break; case SRTO_SNDSYN: - *(bool *)optval = m_config.m_bSynSending; + *(bool *)optval = m_config.bSynSending; optlen = sizeof(bool); break; case SRTO_RCVSYN: - *(bool *)optval = m_config.m_bSynRecving; + *(bool *)optval = m_config.bSynRecving; optlen = sizeof(bool); break; @@ -386,17 +383,17 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_FC: - *(int *)optval = m_config.m_iFlightFlagSize; + *(int *)optval = m_config.iFlightFlagSize; optlen = sizeof(int); break; case SRTO_SNDBUF: - *(int *)optval = m_config.m_iSndBufSize * (m_config.m_iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_config.m_iRcvBufSize * (m_config.m_iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); optlen = sizeof(int); break; @@ -404,63 +401,63 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (optlen < (int)(sizeof(linger))) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - *(linger *)optval = m_config.m_Linger; + *(linger *)optval = m_config.Linger; optlen = sizeof(linger); break; case SRTO_UDP_SNDBUF: - *(int *)optval = m_config.m_iUDPSndBufSize; + *(int *)optval = m_config.iUDPSndBufSize; optlen = sizeof(int); break; case SRTO_UDP_RCVBUF: - *(int *)optval = m_config.m_iUDPRcvBufSize; + *(int *)optval = m_config.iUDPRcvBufSize; optlen = sizeof(int); break; case SRTO_RENDEZVOUS: - *(bool *)optval = m_config.m_bRendezvous; + *(bool *)optval = m_config.bRendezvous; optlen = sizeof(bool); break; case SRTO_SNDTIMEO: - *(int *)optval = m_config.m_iSndTimeOut; + *(int *)optval = m_config.iSndTimeOut; optlen = sizeof(int); break; case SRTO_RCVTIMEO: - *(int *)optval = m_config.m_iRcvTimeOut; + *(int *)optval = m_config.iRcvTimeOut; optlen = sizeof(int); break; case SRTO_REUSEADDR: - *(bool *)optval = m_config.m_bReuseAddr; + *(bool *)optval = m_config.bReuseAddr; optlen = sizeof(bool); break; case SRTO_MAXBW: - if (size_t(optlen) < sizeof(m_config.m_llMaxBW)) + if (size_t(optlen) < sizeof(m_config.llMaxBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - *(int64_t *)optval = m_config.m_llMaxBW; + *(int64_t *)optval = m_config.llMaxBW; optlen = sizeof(int64_t); break; case SRTO_INPUTBW: - if (size_t(optlen) < sizeof(m_config.m_llInputBW)) + if (size_t(optlen) < sizeof(m_config.llInputBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - *(int64_t*)optval = m_config.m_llInputBW; + *(int64_t*)optval = m_config.llInputBW; optlen = sizeof(int64_t); break; case SRTO_MININPUTBW: - if (size_t(optlen) < sizeof (m_config.m_llMinInputBW)) + if (size_t(optlen) < sizeof (m_config.llMinInputBW)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - *(int64_t*)optval = m_config.m_llMinInputBW; + *(int64_t*)optval = m_config.llMinInputBW; optlen = sizeof(int64_t); break; case SRTO_OHEADBW: - *(int32_t *)optval = m_config.m_iOverheadBW; + *(int32_t *)optval = m_config.iOverheadBW; optlen = sizeof(int32_t); break; @@ -480,7 +477,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady()) event |= SRT_EPOLL_IN; leaveCS(m_RecvLock); - if (m_pSndBuffer && (m_config.m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) + if (m_pSndBuffer && (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize())) event |= SRT_EPOLL_OUT; } *(int32_t *)optval = event; @@ -512,7 +509,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bOpened) *(int32_t *)optval = m_pSndQueue->getIpTTL(); else - *(int32_t *)optval = m_config.m_iIpTTL; + *(int32_t *)optval = m_config.iIpTTL; optlen = sizeof(int32_t); break; @@ -520,7 +517,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bOpened) *(int32_t *)optval = m_pSndQueue->getIpToS(); else - *(int32_t *)optval = m_config.m_iIpToS; + *(int32_t *)optval = m_config.iIpToS; optlen = sizeof(int32_t); break; @@ -536,8 +533,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) } // Fallback: return from internal data - strcpy(((char*)optval), m_config.m_BindToDevice.c_str()); - optlen = m_config.m_BindToDevice.size(); + strcpy(((char*)optval), m_config.sBindToDevice.c_str()); + optlen = m_config.sBindToDevice.size(); #else LOGC(smlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -545,12 +542,12 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SENDER: - *(int32_t *)optval = m_config.m_bDataSender; + *(int32_t *)optval = m_config.bDataSender; optlen = sizeof(int32_t); break; case SRTO_TSBPDMODE: - *(int32_t *)optval = m_config.m_bTSBPD; + *(int32_t *)optval = m_config.bTSBPD; optlen = sizeof(int32_t); break; @@ -559,7 +556,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bConnected) *(int32_t *)optval = m_iTsbPdDelay_ms; else - *(int32_t *)optval = m_config.m_iRcvLatency; + *(int32_t *)optval = m_config.iRcvLatency; optlen = sizeof(int32_t); break; @@ -567,7 +564,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_bConnected) *(int32_t *)optval = m_iPeerTsbPdDelay_ms; else - *(int32_t *)optval = m_config.m_iPeerLatency; + *(int32_t *)optval = m_config.iPeerLatency; optlen = sizeof(int32_t); break; @@ -578,7 +575,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SNDDROPDELAY: - *(int32_t *)optval = m_config.m_iSndDropDelay; + *(int32_t *)optval = m_config.iSndDropDelay; optlen = sizeof(int32_t); break; @@ -586,14 +583,14 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (m_pCryptoControl) *(int32_t *)optval = (int32_t) m_pCryptoControl->KeyLen(); // Running Key length. else - *(int32_t *)optval = m_config.m_iSndCryptoKeyLen; // May be 0. + *(int32_t *)optval = m_config.iSndCryptoKeyLen; // May be 0. optlen = sizeof(int32_t); break; case SRTO_KMSTATE: if (!m_pCryptoControl) *(int32_t *)optval = SRT_KM_S_UNSECURED; - else if (m_config.m_bDataSender) + else if (m_config.bDataSender) *(int32_t *)optval = m_pCryptoControl->m_SndKmState; else *(int32_t *)optval = m_pCryptoControl->m_RcvKmState; @@ -617,67 +614,67 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_LOSSMAXTTL: - *(int32_t*)optval = m_config.m_iMaxReorderTolerance; + *(int32_t*)optval = m_config.iMaxReorderTolerance; optlen = sizeof(int32_t); break; case SRTO_NAKREPORT: - *(bool *)optval = m_config.m_bRcvNakReport; + *(bool *)optval = m_config.bRcvNakReport; optlen = sizeof(bool); break; case SRTO_VERSION: - *(int32_t *)optval = m_config.m_lSrtVersion; + *(int32_t *)optval = m_config.uSrtVersion; optlen = sizeof(int32_t); break; case SRTO_PEERVERSION: - *(int32_t *)optval = m_lPeerSrtVersion; + *(int32_t *)optval = m_uPeerSrtVersion; optlen = sizeof(int32_t); break; case SRTO_CONNTIMEO: - *(int*)optval = (int) count_milliseconds(m_config.m_tdConnTimeOut); + *(int*)optval = (int) count_milliseconds(m_config.tdConnTimeOut); optlen = sizeof(int); break; case SRTO_DRIFTTRACER: - *(int*)optval = m_config.m_bDriftTracer; + *(int*)optval = m_config.bDriftTracer; optlen = sizeof(int); break; case SRTO_MINVERSION: - *(uint32_t *)optval = m_config.m_lMinimumPeerSrtVersion; + *(uint32_t *)optval = m_config.uMinimumPeerSrtVersion; optlen = sizeof(uint32_t); break; case SRTO_STREAMID: - if (size_t(optlen) < m_config.m_StreamName.size() + 1) + if (size_t(optlen) < m_config.sStreamName.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_config.m_StreamName.c_str()); - optlen = (int) m_config.m_StreamName.size(); + strcpy((char *)optval, m_config.sStreamName.c_str()); + optlen = (int) m_config.sStreamName.size(); break; case SRTO_CONGESTION: - strcpy((char *)optval, m_config.m_Congestion.c_str()); - optlen = (int) m_config.m_Congestion.size(); + strcpy((char *)optval, m_config.sCongestion.c_str()); + optlen = (int) m_config.sCongestion.size(); break; case SRTO_MESSAGEAPI: optlen = sizeof(bool); - *(bool *)optval = m_config.m_bMessageAPI; + *(bool *)optval = m_config.bMessageAPI; break; case SRTO_PAYLOADSIZE: optlen = sizeof(int); - *(int *)optval = (int) m_config.m_zExpPayloadSize; + *(int *)optval = (int) m_config.zExpPayloadSize; break; #if ENABLE_EXPERIMENTAL_BONDING case SRTO_GROUPCONNECT: optlen = sizeof (int); - *(int*)optval = m_config.m_GroupConnect; + *(int*)optval = m_config.iGroupConnect; break; case SRTO_GROUPTYPE: @@ -688,29 +685,29 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_ENFORCEDENCRYPTION: optlen = sizeof(int32_t); // also with TSBPDMODE and SENDER - *(int32_t *)optval = m_config.m_bEnforcedEnc; + *(int32_t *)optval = m_config.bEnforcedEnc; break; case SRTO_IPV6ONLY: optlen = sizeof(int); - *(int *)optval = m_config.m_iIpV6Only; + *(int *)optval = m_config.iIpV6Only; break; case SRTO_PEERIDLETIMEO: - *(int *)optval = m_config.m_iPeerIdleTimeout; + *(int *)optval = m_config.iPeerIdleTimeout; optlen = sizeof(int); break; case SRTO_PACKETFILTER: - if (size_t(optlen) < m_config.m_PacketFilterConfig.size() + 1) + if (size_t(optlen) < m_config.sPacketFilterConfig.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_config.m_PacketFilterConfig.c_str()); - optlen = (int) m_config.m_PacketFilterConfig.size(); + strcpy((char *)optval, m_config.sPacketFilterConfig.c_str()); + optlen = (int) m_config.sPacketFilterConfig.size(); break; case SRTO_RETRANSMITALGO: - *(int32_t *)optval = m_config.m_iRetransmitAlgo; + *(int32_t *)optval = m_config.iRetransmitAlgo; optlen = sizeof(int32_t); break; @@ -747,7 +744,7 @@ bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) if (that->m_bConnected) return false; - that->m_config.m_StreamName.set(sid); + that->m_config.sStreamName.set(sid); return true; } @@ -757,7 +754,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) if (!that) return ""; - return that->m_config.m_StreamName.str(); + return that->m_config.sStreamName.str(); } // XXX REFACTOR: Make common code for CUDT constructor and clearData, @@ -765,7 +762,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) void CUDT::clearData() { // Initial sequence number, loss, acknowledgement, etc. - int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; + int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -839,8 +836,8 @@ void CUDT::clearData() // should they be set to possibly true. m_bTsbPd = false; m_bGroupTsbPd = false; - m_iTsbPdDelay_ms = m_config.m_iRcvLatency; - m_bTLPktDrop = m_config.m_bTLPktDrop; + m_iTsbPdDelay_ms = m_config.iRcvLatency; + m_bTLPktDrop = m_config.bTLPktDrop; m_bPeerTLPktDrop = false; m_bPeerNakReport = false; @@ -936,7 +933,7 @@ size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, memset((aw_srtdata), 0, sizeof(uint32_t) * srtlen); /* Current version (1.x.x) SRT handshake */ - aw_srtdata[SRT_HS_VERSION] = m_config.m_lSrtVersion; /* Required version */ + aw_srtdata[SRT_HS_VERSION] = m_config.uSrtVersion; /* Required version */ aw_srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); switch (msgtype) @@ -963,10 +960,10 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu // not set TsbPd mode, it will simply ignore the proposed latency (PeerTsbPdDelay), although // if it has received the Rx latency as well, it must honor it and respond accordingly // (the latter is only in case of HSv5 and bidirectional connection). - if (m_config.m_bTSBPD) + if (m_config.bTSBPD) { - m_iTsbPdDelay_ms = m_config.m_iRcvLatency; - m_iPeerTsbPdDelay_ms = m_config.m_iPeerLatency; + m_iTsbPdDelay_ms = m_config.iRcvLatency; + m_iPeerTsbPdDelay_ms = m_config.iPeerLatency; /* * Sent data is real-time, use Time-based Packet Delivery, * set option bit and configured delay @@ -995,7 +992,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu } } - if (m_config.m_bRcvNakReport) + if (m_config.bRcvNakReport) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; // I support SRT_OPT_REXMITFLG. Do you? @@ -1003,7 +1000,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu // Declare the API used. The flag is set for "stream" API because // the older versions will never set this flag, but all old SRT versions use message API. - if (!m_config.m_bMessageAPI) + if (!m_config.bMessageAPI) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; HLOGC(cnlog.Debug, @@ -1074,7 +1071,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu if (m_bTLPktDrop) aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; - if (m_config.m_bRcvNakReport) + if (m_config.bRcvNakReport) { // HSv5: Note that this setting is independent on the value of // m_bPeerNakReport, which represent this setting in the peer. @@ -1088,11 +1085,11 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu * Disabling TLPktDrop in the receiver SRT Handshake Reply prevents the sender * from enabling Too-Late Packet Drop. */ - if (m_lPeerSrtVersion <= SrtVersion(1, 0, 7)) + if (m_uPeerSrtVersion <= SrtVersion(1, 0, 7)) aw_srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; } - if (m_config.m_lSrtVersion >= SrtVersion(1, 2, 0)) + if (m_config.uSrtVersion >= SrtVersion(1, 2, 0)) { if (!m_bPeerRexmitFlag) { @@ -1392,12 +1389,12 @@ bool CUDT::createSrtHandshake( // // Keep 0 in the SRT_HSTYPE_HSFLAGS field, but still advertise PBKEYLEN // in the SRT_HSTYPE_ENCFLAGS field. - w_hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_config.m_iSndCryptoKeyLen); + w_hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_config.iSndCryptoKeyLen); - IF_HEAVY_LOGGING(bool whether = m_config.m_iSndCryptoKeyLen != 0); + IF_HEAVY_LOGGING(bool whether = m_config.iSndCryptoKeyLen != 0); HLOGC(cnlog.Debug, log << "createSrtHandshake: " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); // Note: This is required only when sending a HS message without SRT extensions. // When this is to be sent with SRT extensions, then KMREQ will be attached here @@ -1441,7 +1438,7 @@ bool CUDT::createSrtHandshake( bool have_sid = false; if (srths_cmd == SRT_CMD_HSREQ) { - if (!m_config.m_StreamName.empty()) + if (!m_config.sStreamName.empty()) { have_sid = true; w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; @@ -1459,7 +1456,7 @@ bool CUDT::createSrtHandshake( { peer_filter_capable = true; } - else if (IsSet(m_lPeerSrtFlags, SRT_OPT_FILTERCAP)) + else if (IsSet(m_uPeerSrtFlags, SRT_OPT_FILTERCAP)) { peer_filter_capable = true; } @@ -1478,7 +1475,7 @@ bool CUDT::createSrtHandshake( // possibly confronted with the contents of m_OPT_FECConfigString, // and if it decided to go with filter, it will be nonempty. bool have_filter = false; - if (peer_filter_capable && !m_config.m_PacketFilterConfig.empty()) + if (peer_filter_capable && !m_config.sPacketFilterConfig.empty()) { have_filter = true; w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; @@ -1486,7 +1483,7 @@ bool CUDT::createSrtHandshake( } bool have_congctl = false; - const string sm = m_config.m_Congestion.str(); + const string sm = m_config.sCongestion.str(); if (sm != "" && sm != "live") { have_congctl = true; @@ -1501,7 +1498,7 @@ bool CUDT::createSrtHandshake( // KMRSP must be always sent when: // - Agent set a password, Peer did not send KMREQ: Agent sets snd=NOSECRET. // - Agent set no password, but Peer sent KMREQ: Ageng sets rcv=NOSECRET. - if (m_config.m_CryptoSecret.len > 0 || kmdata_wordsize > 0) + if (m_config.CryptoSecret.len > 0 || kmdata_wordsize > 0) { have_kmreq = true; w_hs.m_iType |= CHandShake::HS_EXT_KMREQ; @@ -1579,7 +1576,7 @@ bool CUDT::createSrtHandshake( // the conclusion packet. size_t size_limit = m_iMaxSRTPayloadSize / 2; - if (m_config.m_StreamName.size() >= size_limit) + if (m_config.sStreamName.size() >= size_limit) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Warn, @@ -1588,11 +1585,11 @@ bool CUDT::createSrtHandshake( } offset += ra_size + 1; - ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_config.m_StreamName.str()); + ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_config.sStreamName.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after SID [" << m_config.m_StreamName.c_str() - << "] length=" << m_config.m_StreamName.size() << " alignedln=" << (4 * ra_size) + log << "createSrtHandshake: after SID [" << m_config.sStreamName.c_str() + << "] length=" << m_config.sStreamName.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " SID size=" << ra_size << " space left: " << (total_ra_size - offset)); } @@ -1614,11 +1611,11 @@ bool CUDT::createSrtHandshake( if (have_filter) { offset += ra_size + 1; - ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_config.m_PacketFilterConfig.str()); + ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_config.sPacketFilterConfig.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after filter [" << m_config.m_PacketFilterConfig.c_str() << "] length=" - << m_config.m_PacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset + log << "createSrtHandshake: after filter [" << m_config.sPacketFilterConfig.c_str() << "] length=" + << m_config.sPacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " filter size=" << ra_size << " space left: " << (total_ra_size - offset)); } @@ -1669,7 +1666,7 @@ bool CUDT::createSrtHandshake( { HLOGC(cnlog.Debug, log << "createSrtHandshake: " - << (m_config.m_CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); + << (m_config.CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); if (srtkm_cmd == SRT_CMD_KMREQ) { bool have_any_keys = false; @@ -1844,7 +1841,7 @@ bool CUDT::processSrtMsg(const CPacket *ctrlpkt) { if (len_out == 1) { - if (m_config.m_bEnforcedEnc) + if (m_config.bEnforcedEnc) { LOGC(cnlog.Warn, log << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])) @@ -1907,8 +1904,8 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t // Prepare the initial runtime values of latency basing on the option values. // They are going to get the value fixed HERE. - m_iTsbPdDelay_ms = m_config.m_iRcvLatency; - m_iPeerTsbPdDelay_ms = m_config.m_iPeerLatency; + m_iTsbPdDelay_ms = m_config.iRcvLatency; + m_iPeerTsbPdDelay_ms = m_config.iPeerLatency; if (bytelen < SRT_CMD_HSREQ_MINSZ) { @@ -1926,12 +1923,12 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t srtdata[SRT_HS_FLAGS], SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); - m_lPeerSrtVersion = srtdata[SRT_HS_VERSION]; - m_lPeerSrtFlags = srtdata[SRT_HS_FLAGS]; + m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; + m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; if (hsv == CUDT::HS_VERSION_UDT4) { - if (m_lPeerSrtVersion >= SRT_VERSION_FEAT_HSv5) + if (m_uPeerSrtVersion >= SRT_VERSION_FEAT_HSv5) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, @@ -1942,7 +1939,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t } else { - if (m_lPeerSrtVersion < SRT_VERSION_FEAT_HSv5) + if (m_uPeerSrtVersion < SRT_VERSION_FEAT_HSv5) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, @@ -1952,31 +1949,31 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t } // Check also if the version satisfies the minimum required version - if (m_lPeerSrtVersion < m_config.m_lMinimumPeerSrtVersion) + if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, - log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion) - << " is too old for requested: " << SrtVersionString(m_config.m_lMinimumPeerSrtVersion) + log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) + << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion) << " - REJECTING"); return SRT_CMD_REJECT; } HLOGC(cnlog.Debug, - log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_lPeerSrtVersion) << " Flags: " << m_lPeerSrtFlags - << "(" << SrtFlagString(m_lPeerSrtFlags) - << ") Min req version:" << SrtVersionString(m_config.m_lMinimumPeerSrtVersion)); + log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_uPeerSrtVersion) << " Flags: " << m_uPeerSrtFlags + << "(" << SrtFlagString(m_uPeerSrtFlags) + << ") Min req version:" << SrtVersionString(m_config.uMinimumPeerSrtVersion)); - m_bPeerRexmitFlag = IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG); + m_bPeerRexmitFlag = IsSet(m_uPeerSrtFlags, SRT_OPT_REXMITFLG); HLOGF(cnlog.Debug, "HSREQ/rcv: peer %s REXMIT flag", m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND"); // Check if both use the same API type. Reject if not. - bool peer_message_api = !IsSet(m_lPeerSrtFlags, SRT_OPT_STREAM); - if (peer_message_api != m_config.m_bMessageAPI) + bool peer_message_api = !IsSet(m_uPeerSrtFlags, SRT_OPT_STREAM); + if (peer_message_api != m_config.bMessageAPI) { m_RejectReason = SRT_REJ_MESSAGEAPI; LOGC(cnlog.Error, - log << "HSREQ/rcv: Agent uses " << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") + log << "HSREQ/rcv: Agent uses " << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, but the Peer declares " << (peer_message_api ? "MESSAGE" : "STREAM") << " API. Not compatible transmission type, rejecting."); return SRT_CMD_REJECT; @@ -2000,7 +1997,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Receiver TSBPD Delay | Sender TSBPD Delay | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND) || IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND) || IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV)) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, @@ -2017,7 +2014,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t const uint32_t latencystr = srtdata[SRT_HS_LATENCY]; - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND)) { // TimeStamp-based Packet Delivery feature enabled if (!isOPT_TsbPd()) @@ -2066,7 +2063,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t // that the peer INITIATOR will receive the data and informs about its predefined // latency. We need to maximize this with our setting of the peer's latency and // record as peer's latency, which will be then sent back with HSRSP. - if (hsv > CUDT::HS_VERSION_UDT4 && IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + if (hsv > CUDT::HS_VERSION_UDT4 && IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV)) { // So, PEER uses TSBPD, set the flag. // NOTE: it doesn't matter, if AGENT uses TSBPD. @@ -2092,12 +2089,12 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t { // This is HSv5, do the same things as required for the sending party in HSv4, // as in HSv5 this can also be a sender. - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TLPKTDROP)) { // Too late packets dropping feature supported m_bPeerTLPktDrop = true; } - if (IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_NAKREPORT)) { // Peer will send Periodic NAK Reports m_bPeerNakReport = true; @@ -2147,22 +2144,22 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t HLOGC(cnlog.Debug, log << "HSRSP/rcv: PEER START TIME already set (derived): " << FormatTime(m_tsRcvPeerStartTime)); } - m_lPeerSrtVersion = srtdata[SRT_HS_VERSION]; - m_lPeerSrtFlags = srtdata[SRT_HS_FLAGS]; + m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; + m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; HLOGF(cnlog.Debug, "HSRSP/rcv: Version: %s Flags: SND:%08X (%s)", - SrtVersionString(m_lPeerSrtVersion).c_str(), - m_lPeerSrtFlags, - SrtFlagString(m_lPeerSrtFlags).c_str()); + SrtVersionString(m_uPeerSrtVersion).c_str(), + m_uPeerSrtFlags, + SrtFlagString(m_uPeerSrtFlags).c_str()); // Basic version check - if (m_lPeerSrtVersion < m_config.m_lMinimumPeerSrtVersion) + if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, - log << "HSRSP/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion) - << " is too old for requested: " << SrtVersionString(m_config.m_lMinimumPeerSrtVersion) + log << "HSRSP/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) + << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion) << " - REJECTING"); return SRT_CMD_REJECT; } @@ -2170,7 +2167,7 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t if (hsv == CUDT::HS_VERSION_UDT4) { // The old HSv4 way: extract just one value and put it under peer. - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV)) { // TsbPd feature enabled m_bPeerTsbPd = true; @@ -2186,7 +2183,7 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t // HSv5 way: extract the receiver latency and sender latency, if used. // PEER WILL RECEIVE TSBPD == AGENT SHALL SEND TSBPD. - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV)) { // TsbPd feature enabled m_bPeerTsbPd = true; @@ -2199,7 +2196,7 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t } // PEER WILL SEND TSBPD == AGENT SHALL RECEIVE TSBPD. - if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND)) { if (!isOPT_TsbPd()) { @@ -2217,21 +2214,21 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t } } - if ((m_config.m_lSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) + if ((m_config.uSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_uPeerSrtFlags, SRT_OPT_TLPKTDROP)) { // Too late packets dropping feature supported m_bPeerTLPktDrop = true; } - if ((m_config.m_lSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) + if ((m_config.uSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_uPeerSrtFlags, SRT_OPT_NAKREPORT)) { // Peer will send Periodic NAK Reports m_bPeerNakReport = true; } - if (m_config.m_lSrtVersion >= SrtVersion(1, 2, 0)) + if (m_config.uSrtVersion >= SrtVersion(1, 2, 0)) { - if (IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG)) + if (IsSet(m_uPeerSrtFlags, SRT_OPT_REXMITFLG)) { // Peer will use REXMIT flag in packet retransmission. m_bPeerRexmitFlag = true; @@ -2408,7 +2405,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, #ifdef SRT_ENABLE_ENCRYPTION if (!m_pCryptoControl->hasPassphrase()) { - if (m_config.m_bEnforcedEnc) + if (m_config.bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -2459,7 +2456,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, { // This means that there was an abnormal encryption situation occurred. // This is inacceptable in case of strict encryption. - if (m_config.m_bEnforcedEnc) + if (m_config.bEnforcedEnc) { if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADSECRET) { @@ -2479,7 +2476,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, else if (cmd == SRT_CMD_KMRSP) { int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); - if (m_config.m_bEnforcedEnc && res == -1) + if (m_config.bEnforcedEnc && res == -1) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, log << "KMRSP failed - rejecting connection as per enforced encryption."); @@ -2506,7 +2503,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // When encryption is not enabled at compile time, behave as if encryption wasn't set, // so accordingly to StrictEncryption flag. - if (m_config.m_bEnforcedEnc) + if (m_config.bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -2524,11 +2521,11 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, bool have_congctl = false; bool have_filter = false; - string agsm = m_config.m_Congestion.str(); + string agsm = m_config.sCongestion.str(); if (agsm == "") { agsm = "live"; - m_config.m_Congestion.set("live", 4); + m_config.sCongestion.set("live", 4); } bool have_group ATR_UNUSED = false; @@ -2574,9 +2571,9 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // Un-swap on big endian machines ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); - m_config.m_StreamName.set(target, bytelen); + m_config.sStreamName.set(target, bytelen); HLOGC(cnlog.Debug, - log << "CONNECTOR'S REQUESTED SID [" << m_config.m_StreamName.c_str() << "] (bytelen=" << bytelen + log << "CONNECTOR'S REQUESTED SID [" << m_config.sStreamName.c_str() << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")"); } else if (cmd == SRT_CMD_CONGESTION) @@ -2701,9 +2698,9 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // Post-checks // Check if peer declared encryption - if (!encrypted && m_config.m_CryptoSecret.len > 0) + if (!encrypted && m_config.CryptoSecret.len > 0) { - if (m_config.m_bEnforcedEnc) + if (m_config.bEnforcedEnc) { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, @@ -2766,13 +2763,13 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) if (!PacketFilter::correctConfig(cfg)) return false; - string thisconf = m_config.m_PacketFilterConfig.str(); + string thisconf = m_config.sPacketFilterConfig.str(); // Now parse your own string, if you have it. if (thisconf != "") { // - for rendezvous, both must be exactly the same (it's unspecified, which will be the first one) - if (m_config.m_bRendezvous && thisconf != confstr) + if (m_config.bRendezvous && thisconf != confstr) { return false; } @@ -2817,7 +2814,7 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) myos << "," << x->first << ":" << x->second; } - m_config.m_PacketFilterConfig.set(myos.str()); + m_config.sPacketFilterConfig.set(myos.str()); HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Effective config: " << thisconf); } @@ -2825,16 +2822,16 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) { // Take the foreign configuration as a good deal. HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Good deal config: " << thisconf); - m_config.m_PacketFilterConfig.set(confstr); + m_config.sPacketFilterConfig.set(confstr); } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; - if (m_config.m_zExpPayloadSize > efc_max_payload_size) + if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, log << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " << efc_max_payload_size << " bytes"); - m_config.m_zExpPayloadSize = efc_max_payload_size; + m_config.zExpPayloadSize = efc_max_payload_size; } return true; @@ -2856,7 +2853,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN int link_weight = SrtHSRequest::HS_GROUP_WEIGHT::unwrap(gd); uint32_t link_flags = SrtHSRequest::HS_GROUP_FLAGS::unwrap(gd); - if (m_config.m_GroupConnect == 0) + if (m_config.iGroupConnect == 0) { m_RejectReason = SRT_REJ_GROUP; LOGC(cnlog.Error, log << "HS/GROUP: this socket is not allowed for group connect."); @@ -3077,8 +3074,8 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l } // Setting non-blocking reading for group socket. - s->core().m_config.m_bSynRecving = false; - s->core().m_config.m_bSynSending = false; + s->core().m_config.bSynRecving = false; + s->core().m_config.bSynSending = false; // Copy of addSocketToGroup. No idea how many parts could be common, not much. @@ -3206,7 +3203,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) ScopedLock cg (m_ConnectionLock); HLOGC(aclog.Debug, log << CONID() << "startConnect: -> " << serv_addr.str() - << (m_config.m_bSynRecving ? " (SYNCHRONOUS)" : " (ASYNCHRONOUS)") << "..."); + << (m_config.bSynRecving ? " (SYNCHRONOUS)" : " (ASYNCHRONOUS)") << "..."); if (!m_bOpened) throw CUDTException(MJ_NOTSUP, MN_NONE, 0); @@ -3223,9 +3220,9 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // register this socket in the rendezvous queue // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this // function - steady_clock::duration ttl = m_config.m_tdConnTimeOut; + steady_clock::duration ttl = m_config.tdConnTimeOut; - if (m_config.m_bRendezvous) + if (m_config.bRendezvous) ttl *= 10; const steady_clock::time_point ttl_time = steady_clock::now() + ttl; @@ -3239,7 +3236,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_ConnReq.m_iType = UDT_DGRAM; // This is my current configuration - if (m_config.m_bRendezvous) + if (m_config.bRendezvous) { // For rendezvous, use version 5 in the waveahand and the cookie. // In case when you get the version 4 waveahand, simply switch to @@ -3256,11 +3253,11 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // This will be also passed to a HSv4 rendezvous, but fortunately the old // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION. - m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_config.m_iSndCryptoKeyLen); - bool whether SRT_ATR_UNUSED = m_config.m_iSndCryptoKeyLen != 0; + m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_config.iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(aclog.Debug, log << "startConnect (rnd): " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); m_RdvState = CHandShake::RDV_WAVING; m_SrtHsSide = HSD_DRAW; // initially not resolved. } @@ -3281,7 +3278,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_RdvState = CHandShake::RDV_INVALID; } - m_ConnReq.m_iMSS = m_config.m_iMSS; + m_ConnReq.m_iMSS = m_config.iMSS; // Defined as the size of the receiver buffer in packets, unless // SRTO_FC has been set to a less value. m_ConnReq.m_iFlightFlagSize = m_config.flightCapacity(); @@ -3357,7 +3354,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) ////////////////////////////////////////////////////// // SYNCHRO BAR ////////////////////////////////////////////////////// - if (!m_config.m_bSynRecving) + if (!m_config.bSynRecving) { HLOGC(cnlog.Debug, log << CONID() << "startConnect: ASYNC MODE DETECTED. Deferring the process to RcvQ:worker"); return; @@ -3394,7 +3391,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << "startConnect: LOOP: time to send (" << count_milliseconds(tdiff) << " > 250 ms). size=" << reqpkt.getLength()); - if (m_config.m_bRendezvous) + if (m_config.bRendezvous) reqpkt.m_iID = m_ConnRes.m_iID; now = steady_clock::now(); @@ -3555,7 +3552,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); e = CUDTException(MJ_SETUP, MN_REJECTED, 0); } - else if ((!m_config.m_bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check + else if ((!m_config.bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check e = CUDTException(MJ_SETUP, MN_SECURITY, 0); } @@ -3575,7 +3572,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << "startConnect: END. Parameters:" " mss=" - << m_config.m_iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() + << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); } @@ -3619,7 +3616,7 @@ bool CUDT::processAsyncConnectRequest(EReadStatus rst, log << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.m_bRendezvous ? 0 : m_ConnRes.m_iID; + request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; bool status = true; @@ -4110,7 +4107,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // For HSv4, the data sender is INITIATOR, and the data receiver is RESPONDER, // regardless of the connecting side affiliation. This will be changed for HSv5. bool bidirectional = false; - HandshakeSide hsd = m_config.m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + HandshakeSide hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER; // (defined here due to 'goto' below). // SRT peer may send the SRT handshake private message (type 0x7fff) before a keep-alive. @@ -4124,7 +4121,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // For the initial form this value should not be checked. bool hsv5 = m_ConnRes.m_iVersion >= HS_VERSION_SRT1; - if (m_config.m_bRendezvous && + if (m_config.bRendezvous && (m_RdvState == CHandShake::RDV_CONNECTED // somehow Rendezvous-v5 switched it to CONNECTED. || !response.isControl() // WAS A PAYLOAD PACKET. || (response.getType() == UMSG_KEEPALIVE) // OR WAS A UMSG_KEEPALIVE message. @@ -4188,7 +4185,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // Yes, we do abort to prevent buffer overrun. Set your MSS correctly // and you'll avoid problems. m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Fatal, log << "MSS size " << m_config.m_iMSS << "exceeds MTU size!"); + LOGC(cnlog.Fatal, log << "MSS size " << m_config.iMSS << "exceeds MTU size!"); return CONN_REJECT; } @@ -4197,7 +4194,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti // The CCryptoControl attached object must be created early // because it will be required to create a conclusion handshake in HSv5 // - if (m_config.m_bRendezvous) + if (m_config.bRendezvous) { // SANITY CHECK: A rendezvous socket should reject any caller requests (it's not a listener) if (m_ConnRes.m_iReqType == URQ_INDUCTION) @@ -4296,7 +4293,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti m_ConnReq.m_extension = true; // For HSv5, the caller is INITIATOR and the listener is RESPONDER. - // The m_config.m_bDataSender value should be completely ignored and the + // The m_config.bDataSender value should be completely ignored and the // connection is always bidirectional. bidirectional = true; hsd = HSD_INITIATOR; @@ -4328,9 +4325,9 @@ bool CUDT::applyResponseSettings() ATR_NOEXCEPT } // Re-configure according to the negotiated values. - m_config.m_iMSS = m_ConnRes.m_iMSS; + m_config.iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; + const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -4560,27 +4557,27 @@ void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t ty if (enc_flags >= 2 && enc_flags <= 4) // 2 = 128, 3 = 192, 4 = 256 { int rcv_pbkeylen = SrtHSRequest::SRT_PBKEYLEN_BITS::wrap(enc_flags); - if (m_config.m_iSndCryptoKeyLen == 0) + if (m_config.iSndCryptoKeyLen == 0) { - m_config.m_iSndCryptoKeyLen = rcv_pbkeylen; + m_config.iSndCryptoKeyLen = rcv_pbkeylen; HLOGC(cnlog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " - << m_config.m_iSndCryptoKeyLen); + << m_config.iSndCryptoKeyLen); } - else if (m_config.m_iSndCryptoKeyLen != rcv_pbkeylen) + else if (m_config.iSndCryptoKeyLen != rcv_pbkeylen) { // Conflict. Use SRTO_SENDER flag to check if this side should accept // the enforcement, otherwise simply let it win. - if (!m_config.m_bDataSender) + if (!m_config.bDataSender) { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.m_iSndCryptoKeyLen << " by " + log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.iSndCryptoKeyLen << " by " << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)"); - m_config.m_iSndCryptoKeyLen = rcv_pbkeylen; + m_config.iSndCryptoKeyLen = rcv_pbkeylen; } else { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - keep " << m_config.m_iSndCryptoKeyLen + log << loghdr << ": PBKEYLEN conflict - keep " << m_config.iSndCryptoKeyLen << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER"); } } @@ -5073,7 +5070,7 @@ void *CUDT::tsbpd(void *param) * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available */ - if (self->m_config.m_bSynRecving) + if (self->m_config.bSynRecving) { recvdata_cc.signal_locked(recv_lock); } @@ -5214,17 +5211,17 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD } else { - hsd = m_config.m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER; } } try { m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); - m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.m_iRcvBufSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.iRcvBufSize); // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); - m_pRcvLossList = new CRcvLossList(m_config.m_iFlightFlagSize); + m_pRcvLossList = new CRcvLossList(m_config.iFlightFlagSize); } catch (...) { @@ -5250,7 +5247,7 @@ void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) { // this is a reponse handshake w_hs.m_iReqType = URQ_CONCLUSION; - w_hs.m_iMSS = m_config.m_iMSS; + w_hs.m_iMSS = m_config.iMSS; w_hs.m_iFlightFlagSize = m_config.flightCapacity(); w_hs.m_iID = m_SocketID; @@ -5274,7 +5271,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly at SRT HS // Uses the smaller MSS between the peers - m_config.m_iMSS = std::min(m_config.m_iMSS, w_hs.m_iMSS); + m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS); // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; @@ -5297,7 +5294,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, rewriteHandshakeData(peer, (w_hs)); - int udpsize = m_config.m_iMSS - CPacket::UDP_HDR_SIZE; + int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; HLOGC(cnlog.Debug, log << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -5455,12 +5452,12 @@ bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) // These data should probably be filled only upon // reception of the conclusion handshake - otherwise // they have outdated values. - m_pCryptoControl->setCryptoSecret(m_config.m_CryptoSecret); + m_pCryptoControl->setCryptoSecret(m_config.CryptoSecret); - if (bidirectional || m_config.m_bDataSender) + if (bidirectional || m_config.bDataSender) { - HLOGC(rslog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_config.m_iSndCryptoKeyLen); - m_pCryptoControl->setCryptoKeylen(m_config.m_iSndCryptoKeyLen); + HLOGC(rslog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen); + m_pCryptoControl->setCryptoKeylen(m_config.iSndCryptoKeyLen); } return m_pCryptoControl->init(side, bidirectional); @@ -5477,28 +5474,28 @@ SRT_REJECT_REASON CUDT::setupCC() // XXX Not sure about that. May happen that AGENT wants // tsbpd mode, but PEER doesn't, even in bidirectional mode. // This way, the reception side should get precedense. - // if (bidirectional || m_config.m_bDataSender || m_bTwoWayData) + // if (bidirectional || m_config.bDataSender || m_bTwoWayData) // m_bPeerTsbPd = m_bTSBPD; // SrtCongestion will retrieve whatever parameters it needs // from *this. - bool res = m_CongCtl.select(m_config.m_Congestion.str()); + bool res = m_CongCtl.select(m_config.sCongestion.str()); if (!res || !m_CongCtl.configure(this)) { return SRT_REJ_CONGESTION; } // Configure filter module - if (!m_config.m_PacketFilterConfig.empty()) + if (!m_config.sPacketFilterConfig.empty()) { // This string, when nonempty, defines that the corrector shall be // configured. Otherwise it's left uninitialized. // At this point we state everything is checked and the appropriate // corrector type is already selected, so now create it. - HLOGC(pflog.Debug, log << "filter: Configuring: " << m_config.m_PacketFilterConfig.c_str()); - if (!m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_config.m_PacketFilterConfig.str())) + HLOGC(pflog.Debug, log << "filter: Configuring: " << m_config.sPacketFilterConfig.c_str()); + if (!m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_config.sPacketFilterConfig.str())) { return SRT_REJ_FILTER; } @@ -5527,7 +5524,7 @@ SRT_REJECT_REASON CUDT::setupCC() m_tsLastSndTime = currtime; HLOGC(rslog.Debug, - log << "setupCC: setting parameters: mss=" << m_config.m_iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize + log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); @@ -5544,7 +5541,7 @@ void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) // Do a fast pre-check first - this simply declares that agent uses HSv5 // and the legacy SRT Handshake is not to be done. Second check is whether // agent is sender (=initiator in HSv4). - if (!isOPT_TsbPd() || !m_config.m_bDataSender) + if (!isOPT_TsbPd() || !m_config.bDataSender) return; if (m_iSndHsRetryCnt <= 0) @@ -5647,23 +5644,23 @@ bool CUDT::closeInternal() // that it's in response to a broken connection. HLOGC(smlog.Debug, log << CONID() << " - closing socket:"); - if (m_config.m_Linger.l_onoff != 0) + if (m_config.Linger.l_onoff != 0) { const steady_clock::time_point entertime = steady_clock::now(); HLOGC(smlog.Debug, log << CONID() << " ... (linger)"); while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && - (steady_clock::now() - entertime < seconds_from(m_config.m_Linger.l_linger))) + (steady_clock::now() - entertime < seconds_from(m_config.Linger.l_linger))) { // linger has been checked by previous close() call and has expired if (m_tsLingerExpiration >= entertime) break; - if (!m_config.m_bSynSending) + if (!m_config.bSynSending) { // if this socket enables asynchronous sending, return immediately and let GC to close it later if (is_zero(m_tsLingerExpiration)) - m_tsLingerExpiration = entertime + seconds_from(m_config.m_Linger.l_linger); + m_tsLingerExpiration = entertime + seconds_from(m_config.Linger.l_linger); HLOGC(smlog.Debug, log << "CUDT::close: linger-nonblocking, setting expire time T=" @@ -5794,7 +5791,7 @@ bool CUDT::closeInternal() m_pCryptoControl.reset(); leaveCS(m_RcvBufferLock); - m_lPeerSrtVersion = SRT_VERSION_UNK; + m_uPeerSrtVersion = SRT_VERSION_UNK; m_tsRcvPeerStartTime = steady_clock::time_point(); m_bOpened = false; @@ -5840,7 +5837,7 @@ int CUDT::receiveBuffer(char *data, int len) return 0; } HLOGC(arlog.Debug, - log << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -5849,14 +5846,14 @@ int CUDT::receiveBuffer(char *data, int len) CSync tscond (m_RcvTsbPdCond, recvguard); if (!m_pRcvBuffer->isRcvDataReady()) { - if (!m_config.m_bSynRecving) + if (!m_config.bSynRecving) { throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } else { /* Kick TsbPd thread to schedule next wakeup (if running) */ - if (m_config.m_iRcvTimeOut < 0) + if (m_config.iRcvTimeOut < 0) { THREAD_PAUSED(); while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) @@ -5869,7 +5866,7 @@ int CUDT::receiveBuffer(char *data, int len) else { const steady_clock::time_point exptime = - steady_clock::now() + milliseconds_from(m_config.m_iRcvTimeOut); + steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); THREAD_PAUSED(); while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) { @@ -5888,13 +5885,13 @@ int CUDT::receiveBuffer(char *data, int len) if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { // See at the beginning - if (!m_config.m_bMessageAPI && m_bShutdown) + if (!m_config.bMessageAPI && m_bShutdown) { HLOGC(arlog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); return 0; } HLOGC(arlog.Debug, - log << (m_config.m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -5919,7 +5916,7 @@ int CUDT::receiveBuffer(char *data, int len) s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } - if ((res <= 0) && (m_config.m_iRcvTimeOut >= 0)) + if ((res <= 0) && (m_config.iRcvTimeOut >= 0)) throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); return res; @@ -5932,7 +5929,7 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) if (!m_bPeerTLPktDrop) return; - if (!m_config.m_bMessageAPI) + if (!m_config.bMessageAPI) { LOGC(aslog.Error, log << "The SRTO_TLPKTDROP flag can only be used with message API."); throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); @@ -5949,9 +5946,9 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) // picture rate would be useful in auto SRT setting for min latency // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable int threshold_ms = 0; - if (m_config.m_iSndDropDelay >= 0) + if (m_config.iSndDropDelay >= 0) { - threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_config.m_iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + + threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + (2 * COMM_SYN_INTERVAL_US / 1000); } @@ -6069,7 +6066,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { SrtCongestion::TransAPI api = SrtCongestion::STA_MESSAGE; CodeMinor mn = MN_INVALMSGAPI; - if (!m_config.m_bMessageAPI) + if (!m_config.bMessageAPI) { api = SrtCongestion::STA_BUFFER; mn = MN_INVALBUFFERAPI; @@ -6097,11 +6094,11 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // out a message of a length that exceeds the total size of the sending // buffer (configurable by SRTO_SNDBUF). - if (m_config.m_bMessageAPI && len > int(m_config.m_iSndBufSize * m_iMaxSRTPayloadSize)) + if (m_config.bMessageAPI && len > int(m_config.iSndBufSize * m_iMaxSRTPayloadSize)) { LOGC(aslog.Error, log << "Message length (" << len << ") exceeds the size of sending buffer: " - << (m_config.m_iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); + << (m_config.iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); } @@ -6130,7 +6127,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) checkNeedDrop((bCongestion)); int minlen = 1; // Minimum sender buffer space required for STREAM API - if (m_config.m_bMessageAPI) + if (m_config.bMessageAPI) { // For MESSAGE API the minimum outgoing buffer space required is // the size that can carry over the whole message as passed here. @@ -6141,14 +6138,14 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { //>>We should not get here if SRT_ENABLE_TLPKTDROP // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop. - if (!m_config.m_bSynSending) + if (!m_config.bSynSending) throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); { // wait here during a blocking sending UniqueLock sendblock_lock (m_SendBlockLock); - if (m_config.m_iSndTimeOut < 0) + if (m_config.iSndTimeOut < 0) { while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) m_SendBlockCond.wait(sendblock_lock); @@ -6156,7 +6153,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) else { const steady_clock::time_point exptime = - steady_clock::now() + milliseconds_from(m_config.m_iSndTimeOut); + steady_clock::now() + milliseconds_from(m_config.iSndTimeOut); THREAD_PAUSED(); while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) { @@ -6186,7 +6183,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) */ if (sndBuffersLeft() < minlen) { - if (m_config.m_iSndTimeOut >= 0) + if (m_config.iSndTimeOut >= 0) throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); // XXX This looks very weird here, however most likely @@ -6220,7 +6217,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) } int size = len; - if (!m_config.m_bMessageAPI) + if (!m_config.bMessageAPI) { // For STREAM API it's allowed to send less bytes than the given buffer. // Just return how many bytes were actually scheduled for writing. @@ -6271,7 +6268,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI); } - if (w_mctrl.srctime && (!m_config.m_bMessageAPI || !m_bTsbPd)) + if (w_mctrl.srctime && (!m_config.bMessageAPI || !m_bTsbPd)) { HLOGC(aslog.Warn, log << "Source time can only be used with TSBPD and Message API enabled. Using default time instead."); @@ -6353,7 +6350,7 @@ int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (m_config.m_bMessageAPI) + if (m_config.bMessageAPI) return receiveMessage(data, len, (w_mctrl)); return receiveBuffer(data, len); @@ -6422,7 +6419,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep if (res == 0) { - if (!m_config.m_bMessageAPI && m_bShutdown) + if (!m_config.bMessageAPI && m_bShutdown) return 0; // Forced to return error instead of throwing exception. if (!by_exception) @@ -6435,7 +6432,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep const int seqdistance = -1; - if (!m_config.m_bSynRecving) + if (!m_config.bSynRecving) { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len); enterCS(m_RcvBufferLock); @@ -6495,7 +6492,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep int res = 0; bool timeout = false; // Do not block forever, check connection status each 1 sec. - const steady_clock::duration recv_timeout = m_config.m_iRcvTimeOut < 0 ? seconds_from(1) : milliseconds_from(m_config.m_iRcvTimeOut); + const steady_clock::duration recv_timeout = m_config.iRcvTimeOut < 0 ? seconds_from(1) : milliseconds_from(m_config.iRcvTimeOut); CSync recv_cond (m_RecvDataCond, recvguard); @@ -6535,7 +6532,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep if (!recv_cond.wait_until(exptime)) { - if (m_config.m_iRcvTimeOut >= 0) // otherwise it's "no timeout set" + if (m_config.iRcvTimeOut >= 0) // otherwise it's "no timeout set" timeout = true; HLOGP(tslog.Debug, "receiveMessage: DATA COND: EXPIRED -- checking connection conditions and rolling again"); @@ -6568,7 +6565,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // Forced to return 0 instead of throwing exception. if (!by_exception) return APIError(MJ_CONNECTION, MN_CONNLOST, 0); - if (!m_config.m_bMessageAPI && m_bShutdown) + if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6604,7 +6601,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // Unblock when required // LOGC(tslog.Debug, "RECVMSG/EXIT RES " << res << " RCVTIMEOUT"); - if ((res <= 0) && (m_config.m_iRcvTimeOut >= 0)) + if ((res <= 0) && (m_config.iRcvTimeOut >= 0)) { // Forced to return -1 instead of throwing exception. if (!by_exception) @@ -6743,7 +6740,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { - if (!m_config.m_bMessageAPI && m_bShutdown) + if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6832,7 +6829,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) { - if (!m_config.m_bMessageAPI && m_bShutdown) + if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6950,9 +6947,9 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->msRTT = (double)m_iRTT / 1000.0; perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; - perf->byteMSS = m_config.m_iMSS; + perf->byteMSS = m_config.iMSS; - perf->mbpsMaxBW = m_config.m_llMaxBW > 0 ? Bps2Mbps(m_config.m_llMaxBW) + perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; @@ -6976,7 +6973,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) } perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize); //< - perf->byteAvailSndBuf = (m_config.m_iSndBufSize - perf->pktSndBuf) * m_config.m_iMSS; + perf->byteAvailSndBuf = (m_config.iSndBufSize - perf->pktSndBuf) * m_config.iMSS; } else { @@ -6988,7 +6985,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_pRcvBuffer) { - perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_config.m_iMSS; + perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_config.iMSS; if (instantaneous) // no need for historical API for Rcv side { perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); @@ -7076,24 +7073,24 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) EInitEvent only_input = arg.get(); // false = TEV_INIT_RESET: in the beginning, or when MAXBW was changed. - if (only_input && m_config.m_llMaxBW) + if (only_input && m_config.llMaxBW) { - HLOGC(rslog.Debug, log << CONID() << "updateCC/TEV_INIT: non-RESET stage and m_config.m_llMaxBW already set to " << m_config.m_llMaxBW); + HLOGC(rslog.Debug, log << CONID() << "updateCC/TEV_INIT: non-RESET stage and m_config.llMaxBW already set to " << m_config.llMaxBW); // Don't change } - else // either m_config.m_llMaxBW == 0 or only_input == TEV_INIT_RESET + else // either m_config.llMaxBW == 0 or only_input == TEV_INIT_RESET { // Use the values: // - if SRTO_MAXBW is >0, use it. // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling // Bytes/s - int bw = m_config.m_llMaxBW != 0 ? m_config.m_llMaxBW : // When used SRTO_MAXBW - m_config.m_llInputBW != 0 ? withOverhead(m_config.m_llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW + int bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW + m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling // Note: setting bw == 0 uses BW_INFINITE value in LiveCC - m_CongCtl->updateBandwidth(m_config.m_llMaxBW, bw); + m_CongCtl->updateBandwidth(m_config.llMaxBW, bw); if (only_input == TEV_INIT_OHEADBW) { @@ -7108,7 +7105,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) } HLOGC(rslog.Debug, - log << CONID() << "updateCC/TEV_INIT: updating BW=" << m_config.m_llMaxBW + log << CONID() << "updateCC/TEV_INIT: updating BW=" << m_config.llMaxBW << (only_input == TEV_INIT_RESET ? " (UNCHANGED)" : only_input == TEV_INIT_OHEADBW ? " (only Overhead)" : " (updated sampling rate)")); @@ -7121,7 +7118,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) { // Specific part done when MaxBW is set to 0 (auto) and InputBW is 0. // This requests internal input rate sampling. - if (m_config.m_llMaxBW == 0 && m_config.m_llInputBW == 0) + if (m_config.llMaxBW == 0 && m_config.llInputBW == 0) { // Get auto-calculated input rate, Bytes per second const int64_t inputbw = m_pSndBuffer->getInputRate(); @@ -7134,7 +7131,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) * Keep previously set maximum in that case (inputbw == 0). */ if (inputbw >= 0) - m_CongCtl->updateBandwidth(0, withOverhead(std::max(m_config.m_llMinInputBW, inputbw))); // Bytes/sec + m_CongCtl->updateBandwidth(0, withOverhead(std::max(m_config.llMinInputBW, inputbw))); // Bytes/sec } } @@ -7550,7 +7547,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } else { - if (m_config.m_bSynRecving) + if (m_config.bSynRecving) { // signal a waiting "recv" call if there is any data available CSync::lock_signal(m_RecvDataCond, m_RecvLock); @@ -7626,13 +7623,13 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) data[ACKD_BANDWIDTH] = m_RcvTimeWindow.getBandwidth(); //>>Patch while incompatible (1.0.2) receiver floating around - if (m_lPeerSrtVersion == SrtVersion(1, 0, 2)) + if (m_uPeerSrtVersion == SrtVersion(1, 0, 2)) { data[ACKD_RCVRATE] = rcvRate; // bytes/sec data[ACKD_XMRATE] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102; } - else if (m_lPeerSrtVersion >= SrtVersion(1, 0, 3)) + else if (m_uPeerSrtVersion >= SrtVersion(1, 0, 3)) { // Normal, currently expected version. data[ACKD_RCVRATE] = rcvRate; // bytes/sec @@ -7734,7 +7731,7 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // insert this socket to snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); - if (m_config.m_bSynSending) + if (m_config.bSynSending) { CSync::lock_signal(m_SendBlockCond, m_SendBlockLock); } @@ -8143,7 +8140,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // inaccurate. Additionally it won't lock if TSBPD mode is off, and // won't update anything. Note that if you set TSBPD mode and use // srt_recvfile (which doesn't make any sense), you'll have a deadlock. - if (m_config.m_bDriftTracer) + if (m_config.bDriftTracer) { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; @@ -8196,7 +8193,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? - || (m_config.m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION + || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION { // The peer side has not received the handshake message, so it keeps querying // resend the handshake packet @@ -8208,12 +8205,12 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // - this is any of URQ_ERROR_* - well... CHandShake initdata; initdata.m_iISN = m_iISN; - initdata.m_iMSS = m_config.m_iMSS; - initdata.m_iFlightFlagSize = m_config.m_iFlightFlagSize; + initdata.m_iMSS = m_config.iMSS; + initdata.m_iFlightFlagSize = m_config.iFlightFlagSize; // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. - initdata.m_iReqType = (!m_config.m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; + initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; initdata.m_iID = m_SocketID; uint32_t kmdata[SRTDATA_MAXSIZE]; @@ -8243,7 +8240,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // and should be added with HSRSP/KMRSP), or it's a belated handshake // of Rendezvous when it has already considered itself connected. // Sanity check - according to the rules, there should be no such situation - if (m_config.m_bRendezvous && m_SrtHsSide == HSD_RESPONDER) + if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER) { LOGC(inlog.Error, log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " @@ -8545,7 +8542,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime continue; } - if (m_bPeerNakReport && m_config.m_iRetransmitAlgo != 0) + if (m_bPeerNakReport && m_config.iRetransmitAlgo != 0) { const steady_clock::time_point tsLastRexmit = m_pSndBuffer->getPacketRexmitTime(offset); if (tsLastRexmit >= time_nak) @@ -9755,7 +9752,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) /// will be set to the tolerance value, which means that later packet retransmission /// will not be required immediately, but only after receiving N next packets that /// do not include the lacking packet. -/// The tolerance is not increased infinitely - it's bordered by m_iMaxReorderTolerance. +/// The tolerance is not increased infinitely - it's bordered by iMaxReorderTolerance. /// This value can be set in options - SRT_LOSSMAXTTL. void CUDT::unlose(const CPacket &packet) { @@ -9786,7 +9783,7 @@ void CUDT::unlose(const CPacket &packet) leaveCS(m_StatsLock); if (seqdiff > m_iReorderTolerance) { - const int new_tolerance = min(seqdiff, m_config.m_iMaxReorderTolerance); + const int new_tolerance = min(seqdiff, m_config.iMaxReorderTolerance); HLOGF(qrlog.Debug, "Belated by %d seqs - Reorder tolerance %s %d", seqdiff, @@ -10104,11 +10101,11 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // Additionally, set this field to a MAGIC value. This field isn't used during INDUCTION // by HSv4 client, HSv5 client can use it to additionally verify that this is a HSv5 listener. // In this field we also advertise the PBKEYLEN value. When 0, it's considered not advertised. - hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.m_iSndCryptoKeyLen); - bool whether SRT_ATR_UNUSED = m_config.m_iSndCryptoKeyLen != 0; + hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, log << "processConnectRequest: " << (whether ? "" : "NOT ") - << " Advertising PBKEYLEN - value = " << m_config.m_iSndCryptoKeyLen); + << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); size_t size = packet.getLength(); hs.store_to((packet.m_pcData), (size)); @@ -10406,11 +10403,11 @@ int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) // by the filter. By this reason they appear often out of order // and for adding them properly the loss list container wasn't // prepared. This then requires some more effort to implement. - if (!m_config.m_bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS) + if (!m_config.bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS) return BECAUSE_NO_REASON; /* - * m_config.m_bRcvNakReport enables NAK reports for SRT. + * m_config.bRcvNakReport enables NAK reports for SRT. * Retransmission based on timeout is bandwidth consuming, * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). @@ -10479,7 +10476,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea return false; // ms -> us - const int PEER_IDLE_TMO_US = m_config.m_iPeerIdleTimeout * 1000; + const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && @@ -10733,7 +10730,7 @@ void CUDT::addEPoll(const int eid) } leaveCS(m_RecvLock); - if (m_config.m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) + if (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize()) { s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } @@ -10908,14 +10905,14 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs } #if ENABLE_EXPERIMENTAL_BONDING - if (have_group && acore->m_config.m_GroupConnect == 0) + if (have_group && acore->m_config.iGroupConnect == 0) { HLOGC(cnlog.Debug, log << "runAcceptHook: REJECTING connection WITHOUT calling the hook - groups not allowed"); return false; } // Update the groupconnect flag - acore->m_config.m_GroupConnect = have_group ? 1 : 0; + acore->m_config.iGroupConnect = have_group ? 1 : 0; acore->m_HSGroupType = gt; #endif diff --git a/srtcore/core.h b/srtcore/core.h index ac489eac0..9b0af91b9 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -300,7 +300,7 @@ class CUDT void addressAndSend(CPacket& pkt); void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); - bool isOPT_TsbPd() const { return m_config.m_bTSBPD; } + bool isOPT_TsbPd() const { return m_config.bTSBPD; } int RTT() const { return m_iRTT; } int RTTVar() const { return m_iRTTVar; } int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } @@ -313,13 +313,13 @@ class CUDT int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } - int64_t maxBandwidth() const { return m_config.m_llMaxBW; } - int MSS() const { return m_config.m_iMSS; } + int64_t maxBandwidth() const { return m_config.llMaxBW; } + int MSS() const { return m_config.iMSS; } uint32_t peerLatency_us() const {return m_iPeerTsbPdDelay_ms * 1000; } - int peerIdleTimeout_ms() const { return m_config.m_iPeerIdleTimeout; } + int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout; } size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } - size_t OPT_PayloadSize() const { return m_config.m_zExpPayloadSize; } + size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -367,7 +367,7 @@ class CUDT const int ps = maxPayloadSize(); if (len == 0) // wierd, can't use non-static data member as default argument! len = ps; - return m_config.m_bMessageAPI ? (len+ps-1)/ps : 1; + return m_config.bMessageAPI ? (len+ps-1)/ps : 1; } int32_t makeTS(const time_point& from_time) const @@ -422,7 +422,7 @@ class CUDT SRTU_PROPERTY_RO(bool, isClosing, m_bClosing); SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); - SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.m_bSynRecving); + SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); @@ -648,7 +648,7 @@ class CUDT int64_t withOverhead(int64_t basebw) { - return (basebw * (100 + m_config.m_iOverheadBW))/100; + return (basebw * (100 + m_config.iOverheadBW))/100; } static double Bps2Mbps(int64_t basebw) @@ -674,7 +674,7 @@ class CUDT int sndBuffersLeft() { - return m_config.m_iSndBufSize - m_pSndBuffer->getCurrBufSize(); + return m_config.iSndBufSize - m_pSndBuffer->getCurrBufSize(); } time_point socketStartTime() @@ -864,8 +864,8 @@ class CUDT int32_t m_iPeerISN; // Initial Sequence Number of the peer side - uint32_t m_lPeerSrtVersion; - uint32_t m_lPeerSrtFlags; + uint32_t m_uPeerSrtVersion; + uint32_t m_uPeerSrtFlags; bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead. diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index a1a24f916..4753e7338 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -584,8 +584,8 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED) // Set security-pending state, if a password was set. m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED; - m_KmPreAnnouncePkt = m_parent->m_config.m_uKmPreAnnouncePkt; - m_KmRefreshRatePkt = m_parent->m_config.m_uKmRefreshRatePkt; + m_KmPreAnnouncePkt = m_parent->m_config.uKmPreAnnouncePkt; + m_KmRefreshRatePkt = m_parent->m_config.uKmRefreshRatePkt; if ( side == HSD_INITIATOR ) { diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 854d3a4c3..f5d537dc7 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -527,16 +527,16 @@ void CUDTGroup::deriveSettings(CUDT* u) // the option is altered on the group. // SRTO_RCVSYN - m_bSynRecving = u->m_config.m_bSynRecving; + m_bSynRecving = u->m_config.bSynRecving; // SRTO_SNDSYN - m_bSynSending = u->m_config.m_bSynSending; + m_bSynSending = u->m_config.bSynSending; // SRTO_RCVTIMEO - m_iRcvTimeOut = u->m_config.m_iRcvTimeOut; + m_iRcvTimeOut = u->m_config.iRcvTimeOut; // SRTO_SNDTIMEO - m_iSndTimeOut = u->m_config.m_iSndTimeOut; + m_iSndTimeOut = u->m_config.iSndTimeOut; // Ok, this really is disgusting, but there's only one way // to properly do it. Would be nice to have some more universal @@ -552,58 +552,58 @@ void CUDTGroup::deriveSettings(CUDT* u) #define IM(option, field) importOption(m_config, option, u->m_config.field) #define IMF(option, field) importOption(m_config, option, u->field) - IM(SRTO_MSS, m_iMSS); - IM(SRTO_FC, m_iFlightFlagSize); + IM(SRTO_MSS, iMSS); + IM(SRTO_FC, iFlightFlagSize); // Nonstandard - importOption(m_config, SRTO_SNDBUF, u->m_config.m_iSndBufSize * (u->m_config.m_iMSS - CPacket::UDP_HDR_SIZE)); - importOption(m_config, SRTO_RCVBUF, u->m_config.m_iRcvBufSize * (u->m_config.m_iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); - IM(SRTO_LINGER, m_Linger); - IM(SRTO_UDP_SNDBUF, m_iUDPSndBufSize); - IM(SRTO_UDP_RCVBUF, m_iUDPRcvBufSize); + IM(SRTO_LINGER, Linger); + IM(SRTO_UDP_SNDBUF, iUDPSndBufSize); + IM(SRTO_UDP_RCVBUF, iUDPRcvBufSize); // SRTO_RENDEZVOUS: impossible to have it set on a listener socket. // SRTO_SNDTIMEO/RCVTIMEO: groupwise setting - IM(SRTO_CONNTIMEO, m_tdConnTimeOut); - IM(SRTO_DRIFTTRACER, m_bDriftTracer); + IM(SRTO_CONNTIMEO, tdConnTimeOut); + IM(SRTO_DRIFTTRACER, bDriftTracer); // Reuseaddr: true by default and should only be true. - IM(SRTO_MAXBW, m_llMaxBW); - IM(SRTO_INPUTBW, m_llInputBW); - IM(SRTO_MININPUTBW, m_llMinInputBW); - IM(SRTO_OHEADBW, m_iOverheadBW); - IM(SRTO_IPTOS, m_iIpToS); - IM(SRTO_IPTTL, m_iIpTTL); - IM(SRTO_TSBPDMODE, m_bTSBPD); - IM(SRTO_RCVLATENCY, m_iRcvLatency); - IM(SRTO_PEERLATENCY, m_iPeerLatency); - IM(SRTO_SNDDROPDELAY, m_iSndDropDelay); - IM(SRTO_PAYLOADSIZE, m_zExpPayloadSize); + IM(SRTO_MAXBW, llMaxBW); + IM(SRTO_INPUTBW, llInputBW); + IM(SRTO_MININPUTBW, llMinInputBW); + IM(SRTO_OHEADBW, iOverheadBW); + IM(SRTO_IPTOS, iIpToS); + IM(SRTO_IPTTL, iIpTTL); + IM(SRTO_TSBPDMODE, bTSBPD); + IM(SRTO_RCVLATENCY, iRcvLatency); + IM(SRTO_PEERLATENCY, iPeerLatency); + IM(SRTO_SNDDROPDELAY, iSndDropDelay); + IM(SRTO_PAYLOADSIZE, zExpPayloadSize); IMF(SRTO_TLPKTDROP, m_bTLPktDrop); - importOption(m_config, SRTO_STREAMID, u->m_config.m_StreamName.str()); + importOption(m_config, SRTO_STREAMID, u->m_config.sStreamName.str()); - IM(SRTO_MESSAGEAPI, m_bMessageAPI); - IM(SRTO_NAKREPORT, m_bRcvNakReport); - IM(SRTO_MINVERSION, m_lMinimumPeerSrtVersion); - IM(SRTO_ENFORCEDENCRYPTION, m_bEnforcedEnc); - IM(SRTO_IPV6ONLY, m_iIpV6Only); - IM(SRTO_PEERIDLETIMEO, m_iPeerIdleTimeout); - IM(SRTO_GROUPSTABTIMEO, m_uStabilityTimeout); + IM(SRTO_MESSAGEAPI, bMessageAPI); + IM(SRTO_NAKREPORT, bRcvNakReport); + IM(SRTO_MINVERSION, uMinimumPeerSrtVersion); + IM(SRTO_ENFORCEDENCRYPTION, bEnforcedEnc); + IM(SRTO_IPV6ONLY, iIpV6Only); + IM(SRTO_PEERIDLETIMEO, iPeerIdleTimeout); + IM(SRTO_GROUPSTABTIMEO, uStabilityTimeout); - importOption(m_config, SRTO_PACKETFILTER, u->m_config.m_PacketFilterConfig.str()); + importOption(m_config, SRTO_PACKETFILTER, u->m_config.sPacketFilterConfig.str()); importOption(m_config, SRTO_PBKEYLEN, u->m_pCryptoControl->KeyLen()); // Passphrase is empty by default. Decipher the passphrase and // store as passphrase option - if (u->m_config.m_CryptoSecret.len) + if (u->m_config.CryptoSecret.len) { - string password((const char*)u->m_config.m_CryptoSecret.str, u->m_config.m_CryptoSecret.len); + string password((const char*)u->m_config.CryptoSecret.str, u->m_config.CryptoSecret.len); m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), password.size())); } - IM(SRTO_KMREFRESHRATE, m_uKmRefreshRatePkt); - IM(SRTO_KMPREANNOUNCE, m_uKmPreAnnouncePkt); + IM(SRTO_KMREFRESHRATE, uKmRefreshRatePkt); + IM(SRTO_KMPREANNOUNCE, uKmPreAnnouncePkt); string cc = u->m_CongCtl.selected_name(); if (cc != "live") diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 25fef4165..332acc22d 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -246,7 +246,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); init.payload_size = parent->OPT_PayloadSize(); - init.rcvbuf_size = parent->m_config.m_iRcvBufSize; + init.rcvbuf_size = parent->m_config.iRcvBufSize; // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 1437d4a1c..e3bf296c1 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1024,7 +1024,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // This queue is used only in case of Async mode (rendezvous or caller-listener). // Synchronous connection requests are handled in startConnect() completely. - if (!i->m_pUDT->m_config.m_bSynRecving) + if (!i->m_pUDT->m_config.bSynRecving) { IF_HEAVY_LOGGING(++debug_nupd); @@ -1536,7 +1536,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // asynchronous connect: call connect here // otherwise wait for the UDT socket to retrieve this packet - if (!u->m_config.m_bSynRecving) + if (!u->m_config.bSynRecving) { HLOGC(cnlog.Debug, log << "AsyncOrRND: packet RESOLVED TO @" << id << " -- continuing as ASYNC CONNECT"); // This is practically same as processConnectResponse, just this applies diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 0c4936f93..e21fbda31 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -76,51 +76,39 @@ struct CSrtMuxerConfig { static const int DEF_UDP_BUFFER_SIZE = 65536; - int m_iIpTTL; - int m_iIpToS; - int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) - bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer + int iIpTTL; + int iIpToS; + int iIpV6Only; // IPV6_V6ONLY option (-1 if not set) + bool bReuseAddr; // reuse an exiting port or not, for UDP multiplexer #ifdef SRT_ENABLE_BINDTODEVICE - std::string m_BindToDevice; + std::string sBindToDevice; #endif - int m_iUDPSndBufSize; // UDP sending buffer size - int m_iUDPRcvBufSize; // UDP receiving buffer size - - // Some shortcuts -#define DEFINEP(psym) \ - char* pv##psym() { return (char*)&m_i##psym; } \ - const char* pv##psym() const { return (char*)&m_i##psym; } - - DEFINEP(UDPSndBufSize); - DEFINEP(UDPRcvBufSize); - DEFINEP(IpV6Only); - DEFINEP(IpTTL); - DEFINEP(IpToS); -#undef DEFINEP + int iUDPSndBufSize; // UDP sending buffer size + int iUDPRcvBufSize; // UDP receiving buffer size bool operator==(const CSrtMuxerConfig& other) const { #define CEQUAL(field) (field == other.field) - return CEQUAL(m_iIpTTL) - && CEQUAL(m_iIpToS) - && CEQUAL(m_iIpV6Only) - && CEQUAL(m_bReuseAddr) + return CEQUAL(iIpTTL) + && CEQUAL(iIpToS) + && CEQUAL(iIpV6Only) + && CEQUAL(bReuseAddr) #ifdef SRT_ENABLE_BINDTODEVICE - && CEQUAL(m_BindToDevice) + && CEQUAL(sBindToDevice) #endif - && CEQUAL(m_iUDPSndBufSize) - && CEQUAL(m_iUDPRcvBufSize); + && CEQUAL(iUDPSndBufSize) + && CEQUAL(iUDPRcvBufSize); #undef CEQUAL } CSrtMuxerConfig() - : m_iIpTTL(-1) /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ - , m_iIpToS(-1) /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ - , m_iIpV6Only(-1) - , m_bReuseAddr(true) // This is default in SRT - , m_iUDPSndBufSize(DEF_UDP_BUFFER_SIZE) - , m_iUDPRcvBufSize(DEF_UDP_BUFFER_SIZE) + : iIpTTL(-1) /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ + , iIpToS(-1) /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ + , iIpV6Only(-1) + , bReuseAddr(true) // This is default in SRT + , iUDPSndBufSize(DEF_UDP_BUFFER_SIZE) + , iUDPRcvBufSize(DEF_UDP_BUFFER_SIZE) { } }; @@ -201,127 +189,127 @@ struct CSrtConfig: CSrtMuxerConfig static const size_t MAX_PFILTER_LENGTH = 64; static const size_t MAX_CONG_LENGTH = 16; - int m_iMSS; // Maximum Segment Size, in bytes - size_t m_zExpPayloadSize; // Expected average payload size (user option) + int iMSS; // Maximum Segment Size, in bytes + size_t zExpPayloadSize; // Expected average payload size (user option) // Options - bool m_bSynSending; // Sending syncronization mode - bool m_bSynRecving; // Receiving syncronization mode - int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side - int m_iSndBufSize; // Maximum UDT sender buffer size - int m_iRcvBufSize; // Maximum UDT receiver buffer size - linger m_Linger; // Linger information on close - bool m_bRendezvous; // Rendezvous connection mode - - duration m_tdConnTimeOut; // connect timeout in milliseconds - bool m_bDriftTracer; - int m_iSndTimeOut; // sending timeout in milliseconds - int m_iRcvTimeOut; // receiving timeout in milliseconds - int64_t m_llMaxBW; // maximum data transfer rate (threshold) + bool bSynSending; // Sending syncronization mode + bool bSynRecving; // Receiving syncronization mode + int iFlightFlagSize; // Maximum number of packets in flight from the peer side + int iSndBufSize; // Maximum UDT sender buffer size + int iRcvBufSize; // Maximum UDT receiver buffer size + linger Linger; // Linger information on close + bool bRendezvous; // Rendezvous connection mode + + duration tdConnTimeOut; // connect timeout in milliseconds + bool bDriftTracer; + int iSndTimeOut; // sending timeout in milliseconds + int iRcvTimeOut; // receiving timeout in milliseconds + int64_t llMaxBW; // maximum data transfer rate (threshold) // These fields keep the options for encryption // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is // created later and takes values from these. - HaiCrypt_Secret m_CryptoSecret; - int m_iSndCryptoKeyLen; + HaiCrypt_Secret CryptoSecret; + int iSndCryptoKeyLen; - // XXX Consider removing. The m_bDataSender stays here + // XXX Consider removing. The bDataSender stays here // in order to maintain the HS side selection in HSv4. - bool m_bDataSender; - - bool m_bMessageAPI; - bool m_bTSBPD; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem) - int m_iRcvLatency; // Agent's Rx latency - int m_iPeerLatency; // Peer's Rx latency for the traffic made by Agent's Tx. - bool m_bTLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx. - int m_iSndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off - bool m_bEnforcedEnc; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected. - int m_GroupConnect; - int m_iPeerIdleTimeout; // Timeout for hearing anything from the peer. - uint32_t m_uStabilityTimeout; - int m_iRetransmitAlgo; - - int64_t m_llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth - int64_t m_llMinInputBW; // Minimum input stream rate estimate (bytes/sec) - int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0) - bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports - int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance + bool bDataSender; + + bool bMessageAPI; + bool bTSBPD; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem) + int iRcvLatency; // Agent's Rx latency + int iPeerLatency; // Peer's Rx latency for the traffic made by Agent's Tx. + bool bTLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx. + int iSndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off + bool bEnforcedEnc; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected. + int iGroupConnect; // 1 - allow group connections + int iPeerIdleTimeout; // Timeout for hearing anything from the peer. + uint32_t uStabilityTimeout; + int iRetransmitAlgo; + + int64_t llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth + int64_t llMinInputBW; // Minimum input stream rate estimate (bytes/sec) + int iOverheadBW; // Percent above input stream rate (applies if llMaxBW == 0) + bool bRcvNakReport; // Enable Receiver Periodic NAK Reports + int iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance // For the use of CCryptoControl // HaiCrypt configuration - unsigned int m_uKmRefreshRatePkt; - unsigned int m_uKmPreAnnouncePkt; + unsigned int uKmRefreshRatePkt; + unsigned int uKmPreAnnouncePkt; - uint32_t m_lSrtVersion; - uint32_t m_lMinimumPeerSrtVersion; + uint32_t uSrtVersion; + uint32_t uMinimumPeerSrtVersion; - StringStorage m_Congestion; - StringStorage m_PacketFilterConfig; - StringStorage m_StreamName; + StringStorage sCongestion; + StringStorage sPacketFilterConfig; + StringStorage sStreamName; // Shortcuts and utilities int32_t flightCapacity() { - return std::min(m_iRcvBufSize, m_iFlightFlagSize); + return std::min(iRcvBufSize, iFlightFlagSize); } CSrtConfig() - : m_iMSS(DEF_MSS) - , m_zExpPayloadSize(SRT_LIVE_DEF_PLSIZE) - , m_bSynSending(true) - , m_bSynRecving(true) - , m_iFlightFlagSize(DEF_FLIGHT_SIZE) - , m_iSndBufSize(DEF_BUFFER_SIZE) - , m_iRcvBufSize(DEF_BUFFER_SIZE) - , m_bRendezvous(false) - , m_tdConnTimeOut(srt::sync::seconds_from(DEF_CONNTIMEO_S)) - , m_bDriftTracer(true) - , m_iSndTimeOut(-1) - , m_iRcvTimeOut(-1) - , m_llMaxBW(-1) - , m_bDataSender(false) - , m_bMessageAPI(true) - , m_bTSBPD(true) - , m_iRcvLatency(SRT_LIVE_DEF_LATENCY_MS) - , m_iPeerLatency(0) - , m_bTLPktDrop(true) - , m_iSndDropDelay(0) - , m_bEnforcedEnc(true) - , m_GroupConnect(0) - , m_iPeerIdleTimeout(COMM_RESPONSE_TIMEOUT_MS) - , m_uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) - , m_iRetransmitAlgo(0) - , m_llInputBW(0) - , m_llMinInputBW(0) - , m_iOverheadBW(25) - , m_bRcvNakReport(true) - , m_iMaxReorderTolerance(0) // Sensible optimal value is 10, 0 preserves old behavior - , m_uKmRefreshRatePkt(0) - , m_uKmPreAnnouncePkt(0) - , m_lSrtVersion(SRT_DEF_VERSION) - , m_lMinimumPeerSrtVersion(SRT_VERSION_MAJ1) + : iMSS(DEF_MSS) + , zExpPayloadSize(SRT_LIVE_DEF_PLSIZE) + , bSynSending(true) + , bSynRecving(true) + , iFlightFlagSize(DEF_FLIGHT_SIZE) + , iSndBufSize(DEF_BUFFER_SIZE) + , iRcvBufSize(DEF_BUFFER_SIZE) + , bRendezvous(false) + , tdConnTimeOut(srt::sync::seconds_from(DEF_CONNTIMEO_S)) + , bDriftTracer(true) + , iSndTimeOut(-1) + , iRcvTimeOut(-1) + , llMaxBW(-1) + , bDataSender(false) + , bMessageAPI(true) + , bTSBPD(true) + , iRcvLatency(SRT_LIVE_DEF_LATENCY_MS) + , iPeerLatency(0) + , bTLPktDrop(true) + , iSndDropDelay(0) + , bEnforcedEnc(true) + , iGroupConnect(0) + , iPeerIdleTimeout(COMM_RESPONSE_TIMEOUT_MS) + , uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) + , iRetransmitAlgo(0) + , llInputBW(0) + , llMinInputBW(0) + , iOverheadBW(25) + , bRcvNakReport(true) + , iMaxReorderTolerance(0) // Sensible optimal value is 10, 0 preserves old behavior + , uKmRefreshRatePkt(0) + , uKmPreAnnouncePkt(0) + , uSrtVersion(SRT_DEF_VERSION) + , uMinimumPeerSrtVersion(SRT_VERSION_MAJ1) { // Default UDT configurations - m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; + iUDPRcvBufSize = iRcvBufSize * iMSS; // Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option // for other modes. - m_Linger.l_onoff = 0; - m_Linger.l_linger = 0; - m_CryptoSecret.len = 0; - m_iSndCryptoKeyLen = 0; + Linger.l_onoff = 0; + Linger.l_linger = 0; + CryptoSecret.len = 0; + iSndCryptoKeyLen = 0; // Default congestion is "live". // Available builtin congestions: "file". // Others can be registerred. - m_Congestion.set("live", 4); + sCongestion.set("live", 4); } ~CSrtConfig() { // Wipeout critical data - memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); + memset(&CryptoSecret, 0, sizeof(CryptoSecret)); } int set(SRT_SOCKOPT optName, const void* val, int size); @@ -410,13 +398,13 @@ struct CSrtConfigSetter if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_iMSS = ival; + co.iMSS = ival; // Packet size cannot be greater than UDP buffer size - if (co.m_iMSS > co.m_iUDPSndBufSize) - co.m_iMSS = co.m_iUDPSndBufSize; - if (co.m_iMSS > co.m_iUDPRcvBufSize) - co.m_iMSS = co.m_iUDPRcvBufSize; + if (co.iMSS > co.iUDPSndBufSize) + co.iMSS = co.iUDPSndBufSize; + if (co.iMSS > co.iUDPRcvBufSize) + co.iMSS = co.iUDPRcvBufSize; } }; @@ -429,7 +417,7 @@ struct CSrtConfigSetter if (fc < 1) throw CUDTException(MJ_NOTSUP, MN_INVAL); - co.m_iFlightFlagSize = std::min(fc, +co.DEF_MAX_FLIGHT_PKT); + co.iFlightFlagSize = std::min(fc, +co.DEF_MAX_FLIGHT_PKT); } }; @@ -442,7 +430,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_iSndBufSize = bs / (co.m_iMSS - CPacket::UDP_HDR_SIZE); + co.iSndBufSize = bs / (co.iMSS - CPacket::UDP_HDR_SIZE); } }; @@ -456,17 +444,17 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.m_iMSS - CPacket::UDP_HDR_SIZE; + const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; // XXX This magic 32 deserves some constant if (val > mssin_size * co.DEF_MAX_FLIGHT_PKT) - co.m_iRcvBufSize = val / mssin_size; + co.iRcvBufSize = val / mssin_size; else - co.m_iRcvBufSize = co.DEF_MAX_FLIGHT_PKT; + co.iRcvBufSize = co.DEF_MAX_FLIGHT_PKT; // recv buffer MUST not be greater than FC size - if (co.m_iRcvBufSize > co.m_iFlightFlagSize) - co.m_iRcvBufSize = co.m_iFlightFlagSize; + if (co.iRcvBufSize > co.iFlightFlagSize) + co.iRcvBufSize = co.iFlightFlagSize; } }; @@ -475,7 +463,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_Linger = cast_optval(optval, optlen); + co.Linger = cast_optval(optval, optlen); } }; @@ -484,7 +472,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iUDPSndBufSize = std::max(co.m_iMSS, cast_optval(optval, optlen)); + co.iUDPSndBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); } }; @@ -493,7 +481,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iUDPRcvBufSize = std::max(co.m_iMSS, cast_optval(optval, optlen)); + co.iUDPRcvBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); } }; template<> @@ -501,7 +489,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bRendezvous = cast_optval(optval, optlen); + co.bRendezvous = cast_optval(optval, optlen); } }; @@ -510,7 +498,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iSndTimeOut = cast_optval(optval, optlen); + co.iSndTimeOut = cast_optval(optval, optlen); } }; @@ -519,7 +507,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iRcvTimeOut = cast_optval(optval, optlen); + co.iRcvTimeOut = cast_optval(optval, optlen); } }; @@ -528,7 +516,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bSynSending = cast_optval(optval, optlen); + co.bSynSending = cast_optval(optval, optlen); } }; template<> @@ -536,7 +524,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bSynRecving = cast_optval(optval, optlen); + co.bSynRecving = cast_optval(optval, optlen); } }; @@ -545,7 +533,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bReuseAddr = cast_optval(optval, optlen); + co.bReuseAddr = cast_optval(optval, optlen); } }; @@ -558,7 +546,7 @@ struct CSrtConfigSetter if (val < -1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_llMaxBW = val; + co.llMaxBW = val; } }; @@ -570,7 +558,7 @@ struct CSrtConfigSetter int val = cast_optval(optval, optlen); if (!(val == -1) && !((val >= 1) && (val <= 255))) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_iIpTTL = cast_optval(optval); + co.iIpTTL = cast_optval(optval); } }; template<> @@ -578,7 +566,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iIpToS = cast_optval(optval, optlen); + co.iIpToS = cast_optval(optval, optlen); } }; @@ -603,7 +591,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - co.m_BindToDevice = val; + co.sBindToDevice = val; #else (void)co; // prevent warning (void)optval; @@ -622,7 +610,7 @@ struct CSrtConfigSetter const int64_t val = cast_optval(optval, optlen); if (val < 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_llInputBW = val; + co.llInputBW = val; } }; template<> @@ -633,7 +621,7 @@ struct CSrtConfigSetter const int64_t val = cast_optval(optval, optlen); if (val < 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_llMinInputBW = val; + co.llMinInputBW = val; } }; template<> @@ -644,7 +632,7 @@ struct CSrtConfigSetter const int32_t val = cast_optval(optval, optlen); if (val < 5 || val > 100) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_iOverheadBW = val; + co.iOverheadBW = val; } }; template<> @@ -652,7 +640,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bDataSender = cast_optval(optval, optlen); + co.bDataSender = cast_optval(optval, optlen); } }; template<> @@ -660,7 +648,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bTSBPD = cast_optval(optval, optlen); + co.bTSBPD = cast_optval(optval, optlen); } }; template<> @@ -668,8 +656,8 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iRcvLatency = cast_optval(optval, optlen); - co.m_iPeerLatency = cast_optval(optval); + co.iRcvLatency = cast_optval(optval, optlen); + co.iPeerLatency = cast_optval(optval); } }; template<> @@ -677,7 +665,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iRcvLatency = cast_optval(optval, optlen); + co.iRcvLatency = cast_optval(optval, optlen); } }; template<> @@ -685,7 +673,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iPeerLatency = cast_optval(optval, optlen); + co.iPeerLatency = cast_optval(optval, optlen); } }; template<> @@ -693,7 +681,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bTLPktDrop = cast_optval(optval, optlen); + co.bTLPktDrop = cast_optval(optval, optlen); } }; template<> @@ -703,7 +691,7 @@ struct CSrtConfigSetter { // Surprise: you may be connected to alter this option. // The application may manipulate this option on sender while transmitting. - co.m_iSndDropDelay = cast_optval(optval, optlen); + co.iSndDropDelay = cast_optval(optval, optlen); } }; template<> @@ -718,10 +706,10 @@ struct CSrtConfigSetter if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - memset(&co.m_CryptoSecret, 0, sizeof(co.m_CryptoSecret)); - co.m_CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; - co.m_CryptoSecret.len = (optlen <= (int)sizeof(co.m_CryptoSecret.str) ? optlen : (int)sizeof(co.m_CryptoSecret.str)); - memcpy((co.m_CryptoSecret.str), optval, co.m_CryptoSecret.len); + memset(&co.CryptoSecret, 0, sizeof(co.CryptoSecret)); + co.CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; + co.CryptoSecret.len = (optlen <= (int)sizeof(co.CryptoSecret.str) ? optlen : (int)sizeof(co.CryptoSecret.str)); + memcpy((co.CryptoSecret.str), optval, co.CryptoSecret.len); #else (void)co; // prevent warning (void)optval; @@ -778,7 +766,7 @@ struct CSrtConfigSetter // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case // of rendezvous, as it is the matter of luck who of them will // eventually become the initiator). This way the receiver - // being an initiator will set m_iSndCryptoKeyLen before setting + // being an initiator will set iSndCryptoKeyLen before setting // up KMREQ for sending to the sender-responder. // // Note that in HSv5 if both sides set PBKEYLEN, the responder @@ -786,7 +774,7 @@ struct CSrtConfigSetter // will be the one advertised by the responder). If none sets, // PBKEYLEN will default to 16. - co.m_iSndCryptoKeyLen = v; + co.iSndCryptoKeyLen = v; #else (void)co; // prevent warning (void)optval; @@ -802,7 +790,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bRcvNakReport = cast_optval(optval, optlen); + co.bRcvNakReport = cast_optval(optval, optlen); } }; @@ -812,7 +800,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { using namespace srt::sync; - co.m_tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); + co.tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); } }; @@ -821,7 +809,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bDriftTracer = cast_optval(optval, optlen); + co.bDriftTracer = cast_optval(optval, optlen); } }; @@ -830,7 +818,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iMaxReorderTolerance = cast_optval(optval, optlen); + co.iMaxReorderTolerance = cast_optval(optval, optlen); } }; @@ -839,7 +827,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_lSrtVersion = cast_optval(optval, optlen); + co.uSrtVersion = cast_optval(optval, optlen); } }; @@ -848,7 +836,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_lMinimumPeerSrtVersion = cast_optval(optval, optlen); + co.uMinimumPeerSrtVersion = cast_optval(optval, optlen); } }; @@ -860,7 +848,7 @@ struct CSrtConfigSetter if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_StreamName.set((const char*)optval, optlen); + co.sStreamName.set((const char*)optval, optlen); } }; @@ -883,7 +871,7 @@ struct CSrtConfigSetter if (!res) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.m_Congestion.set(val); + co.sCongestion.set(val); } }; @@ -892,7 +880,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bMessageAPI = cast_optval(optval, optlen); + co.bMessageAPI = cast_optval(optval, optlen); } }; @@ -909,13 +897,13 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (!co.m_PacketFilterConfig.empty()) + if (!co.sPacketFilterConfig.empty()) { // This means that the filter might have been installed before, // and the fix to the maximum payload size was already applied. // This needs to be checked now. SrtFilterConfig fc; - if (!ParseFilterConfig(co.m_PacketFilterConfig.str(), fc)) + if (!ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) { // Break silently. This should not happen LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); @@ -923,7 +911,7 @@ struct CSrtConfigSetter } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.m_zExpPayloadSize > efc_max_payload_size) + if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size @@ -932,7 +920,7 @@ struct CSrtConfigSetter } } - co.m_zExpPayloadSize = cast_optval(optval, optlen); + co.zExpPayloadSize = cast_optval(optval, optlen); } }; @@ -953,17 +941,17 @@ struct CSrtConfigSetter // - linger: off // - congctl: live // - extraction method: message (reading call extracts one message) - co.m_bTSBPD = true; - co.m_iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; - co.m_iPeerLatency = 0; - co.m_bTLPktDrop = true; - co.m_iSndDropDelay = 0; - co.m_bMessageAPI = true; - co.m_bRcvNakReport = true; - co.m_zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; - co.m_Linger.l_onoff = 0; - co.m_Linger.l_linger = 0; - co.m_Congestion.set("live", 4); + co.bTSBPD = true; + co.iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; + co.iPeerLatency = 0; + co.bTLPktDrop = true; + co.iSndDropDelay = 0; + co.bMessageAPI = true; + co.bRcvNakReport = true; + co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + co.Linger.l_onoff = 0; + co.Linger.l_linger = 0; + co.sCongestion.set("live", 4); break; case SRTT_FILE: @@ -973,17 +961,17 @@ struct CSrtConfigSetter // - linger: 2 minutes (180s) // - congctl: file (original UDT congestion control) // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) - co.m_bTSBPD = false; - co.m_iRcvLatency = 0; - co.m_iPeerLatency = 0; - co.m_bTLPktDrop = false; - co.m_iSndDropDelay = -1; - co.m_bMessageAPI = false; - co.m_bRcvNakReport = false; - co.m_zExpPayloadSize = 0; // use maximum - co.m_Linger.l_onoff = 1; - co.m_Linger.l_linger = CSrtConfig::DEF_LINGER_S; - co.m_Congestion.set("file", 4); + co.bTSBPD = false; + co.iRcvLatency = 0; + co.iPeerLatency = 0; + co.bTLPktDrop = false; + co.iSndDropDelay = -1; + co.bMessageAPI = false; + co.bRcvNakReport = false; + co.zExpPayloadSize = 0; // use maximum + co.Linger.l_onoff = 1; + co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; + co.sCongestion.set("file", 4); break; default: @@ -998,7 +986,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_GroupConnect = cast_optval(optval, optlen); + co.iGroupConnect = cast_optval(optval, optlen); } }; #endif @@ -1012,13 +1000,13 @@ struct CSrtConfigSetter // If you first change the KMREFRESHRATE, KMPREANNOUNCE // will be set to the maximum allowed value - co.m_uKmRefreshRatePkt = cast_optval(optval, optlen); - if (co.m_uKmPreAnnouncePkt == 0 || co.m_uKmPreAnnouncePkt > (co.m_uKmRefreshRatePkt - 1) / 2) + co.uKmRefreshRatePkt = cast_optval(optval, optlen); + if (co.uKmPreAnnouncePkt == 0 || co.uKmPreAnnouncePkt > (co.uKmRefreshRatePkt - 1) / 2) { - co.m_uKmPreAnnouncePkt = (co.m_uKmRefreshRatePkt - 1) / 2; + co.uKmPreAnnouncePkt = (co.uKmRefreshRatePkt - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << co.m_uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" - << std::hex << co.m_uKmPreAnnouncePkt); + log << "SRTO_KMREFRESHRATE=0x" << std::hex << co.uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" + << std::hex << co.uKmPreAnnouncePkt); } } }; @@ -1031,7 +1019,7 @@ struct CSrtConfigSetter using namespace srt_logging; const int val = cast_optval(optval, optlen); - const int kmref = co.m_uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.m_uKmRefreshRatePkt; + const int kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; if (val > (kmref - 1) / 2) { LOGC(aclog.Error, @@ -1040,7 +1028,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - co.m_uKmPreAnnouncePkt = val; + co.uKmPreAnnouncePkt = val; } }; @@ -1049,7 +1037,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_bEnforcedEnc = cast_optval(optval, optlen); + co.bEnforcedEnc = cast_optval(optval, optlen); } }; @@ -1058,7 +1046,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iPeerIdleTimeout = cast_optval(optval, optlen); + co.iPeerIdleTimeout = cast_optval(optval, optlen); } }; @@ -1067,7 +1055,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iIpV6Only = cast_optval(optval, optlen); + co.iIpV6Only = cast_optval(optval, optlen); } }; @@ -1090,15 +1078,15 @@ struct CSrtConfigSetter } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.m_zExpPayloadSize > efc_max_payload_size) + if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Warn, log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " << efc_max_payload_size << " bytes"); - co.m_zExpPayloadSize = efc_max_payload_size; + co.zExpPayloadSize = efc_max_payload_size; } - co.m_PacketFilterConfig.set(arg); + co.sPacketFilterConfig.set(arg); } }; @@ -1117,7 +1105,7 @@ struct CSrtConfigSetter // Search if you already have SRTO_PEERIDLETIMEO set - const int idletmo = co.m_iPeerIdleTimeout; + const int idletmo = co.iPeerIdleTimeout; // Both are in milliseconds. // This option is RECORDED in microseconds, while @@ -1130,7 +1118,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - co.m_uStabilityTimeout = val * 1000; + co.uStabilityTimeout = val * 1000; } }; #endif @@ -1140,7 +1128,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.m_iRetransmitAlgo = cast_optval(optval, optlen); + co.iRetransmitAlgo = cast_optval(optval, optlen); } }; From de54e65e66d5c86278abfb80d30435e67e5d0a5e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 8 Mar 2021 09:46:58 +0100 Subject: [PATCH 083/790] [core] Fixed wrong check of common FEC configuration (#1826) Added unit tests --- docs/APISocketOptions.md | 64 +++- docs/packet-filtering-and-fec.md | 7 +- srtcore/common.cpp | 5 +- srtcore/core.cpp | 27 +- srtcore/core.h | 3 +- srtcore/fec.cpp | 125 +++++-- srtcore/fec.h | 3 + srtcore/packetfilter.cpp | 72 +++- srtcore/packetfilter.h | 28 +- srtcore/packetfilter_api.h | 2 +- srtcore/queue.cpp | 3 + srtcore/queue.h | 6 +- srtcore/socketconfig.h | 12 +- test/test_fec_rebuilding.cpp | 616 +++++++++++++++++++++++++++++++ testing/testmedia.cpp | 5 +- 15 files changed, 914 insertions(+), 64 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 780b92228..f6ca590e1 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -228,7 +228,7 @@ The following table lists SRT socket options in alphabetical order. Option detai | [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | -| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | +| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | | [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | | [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | | [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | @@ -871,17 +871,61 @@ and break quickly at any rise in packet loss. | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PACKETFILTER` | 1.4.0 | pre | `string` | | "" | [512] | W | GSD | +| `SRTO_PACKETFILTER` | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | Set up the packet filter. The string must match appropriate syntax for packet -filter setup. - -As there can only be one configuration for both parties, it is recommended that -one party defines the full configuration while the other only defines the matching -packet filter type (for example, one sets `fec,cols:10,rows:-5,layout:staircase` -and the other just `fec`). Both parties can also set this option to the same value. -The packet filter function will attempt to merge configuration definitions, but if -the options specified are in conflict, the connection will be rejected. +filter setup. Note also that: + +* The configuration is case-sentitive (e.g. "FEC,Cols:20" is not valid). +* Setting this option will fail if you use an unknown filter type. + +An empty value for this option means that for this connection the filter isn't +required, but it will accept any filter settings if provided by the peer. If +this option is changed by both parties simultaneously, the result will be a +configuration integrating parameters from both parties, that is: + +* parameters provided by both parties are accepted, if they are identical +* parameters that are set only on one side will have the value defined by that side +* parameters not set in either side will be set as default + +The connection will be rejected with `SRT_REJ_FILTER` code in the following cases: + +* both sides define a different packet filter type +* for the same key two different values were provided by both sides +* mandatory parameters weren't provided by either side + +In case of the built-in `fec` filter, the mandatory parameter is `cols`, all +others have their default values. For example, the configuration specified +as `fec,cols:10` is `fec,cols:10,rows:1,arq:onreq,layout:even`. See how to +[configure the FEC Filter](packet-filtering-and-fec.md#configuring-the-fec-filter). + +Below in the table are examples for the built-in `fec` filter. Note that the +negotiated config need not have parameters in the given order. + +Cases when negotiation succeeds: + +| Peer A | Peer B | Negotiated Config +|----------------------|---------------------|------------------------------------------------------ +| (no filter) | (no filter) | +| fec,cols:10 | fec | fec,cols:10,rows:1,arq:onreq,layout:even +| fec,cols:10 | fec,cols:10,rows:20 | fec,cols:10,rows:20,arq:onreq,layout:even +| fec,layout:staircase | fec,cols:10 | fec,cols:10,rows:1,arq:onreq,layout:staircase + +In these cases the configuration is rejected with SRT_REJ_FILTER code: + +| Peer A | Peer B | Error reason +|-----------------------|---------------------|-------------------------- +| fec | (no filter) | missing `cols` parameter +| fec,rows:20,arq:never | fec,layout:even | missing `cols` parameter +| fec,cols:20 | fec,cols:10 | `cols` parameter value conflict +| fec,cols:20,rows:20 | fec,cols:20,rows:10 | `rows` parameter value conflict + +In general it is recommended that one party defines the full configuration, +while the other keeps this value empty. + +Reading this option after the connection is established will return the full +configuration that has been agreed upon by both parties (including default +values). For details, see [Packet Filtering & FEC](packet-filtering-and-fec.md). diff --git a/docs/packet-filtering-and-fec.md b/docs/packet-filtering-and-fec.md index acb12405d..c834d9934 100644 --- a/docs/packet-filtering-and-fec.md +++ b/docs/packet-filtering-and-fec.md @@ -42,13 +42,14 @@ general syntax: ``` The parts of this syntax are separated by commas. The first part is the name of the filter. This is followed by one or more key:value pairs, the interpretation -of which depends on the filter type. +of which depends on the filter type. Note that all keys and values are +case-sensitive. You can try this out using the `SRTO_PACKETFILTER` option, or the `packetfilter` parameter in an SRT URI in the applications. The packet filter framework is open for extensions so that users may register -their own filters. SRT provides also one builtin filter named "fec". This +their own filters. SRT provides also one built-in filter named "fec". This filter implements the FEC mechanism, as described in SMPTE 2022-1-2007. ![SRT packet filter mechanism](/docs/images/packet-filter-mechanism.png) @@ -56,7 +57,7 @@ filter implements the FEC mechanism, as described in SMPTE 2022-1-2007. On the input side, filtering occurs at the moment when a packet is extracted from the send buffer. A filter may then do two things: -* alter the packet before inserting it into the SRT channel (the builtin "fec" +* alter the packet before inserting it into the SRT channel (the built-in "fec" filter doesn't do it, though) * insert another packet (such as an FEC control packet) into the channel ahead diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 564bd9e35..31b1bebe0 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -462,7 +462,7 @@ extern const char* const srt_rejectreason_msg [] = { "Password required or unexpected", "MessageAPI/StreamAPI collision", "Congestion controller type collision", - "Packet Filter type collision", + "Packet Filter settings error", "Group settings collision", "Connection timeout" }; @@ -495,7 +495,8 @@ bool SrtParseConfig(string s, SrtConfig& w_config) Split(*i, ':', back_inserter(keyval)); if (keyval.size() != 2) return false; - w_config.parameters[keyval[0]] = keyval[1]; + if (keyval[1] != "") + w_config.parameters[keyval[0]] = keyval[1]; } return true; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2d5cb2d86..df40ef0d5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2648,6 +2648,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, if (!checkApplyFilterConfig(fltcfg)) { + m_RejectReason = SRT_REJ_FILTER; LOGC(cnlog.Error, log << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected"); return false; } @@ -2755,7 +2756,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, bool CUDT::checkApplyFilterConfig(const std::string &confstr) { SrtFilterConfig cfg; - if (!ParseFilterConfig(confstr, cfg)) + if (!ParseFilterConfig(confstr, (cfg))) return false; // Now extract the type, if present, and @@ -2775,7 +2776,7 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) } SrtFilterConfig mycfg; - if (!ParseFilterConfig(thisconf, mycfg)) + if (!ParseFilterConfig(thisconf, (mycfg))) return false; // Check only if both have set a filter of the same type. @@ -2795,12 +2796,8 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) } else { - // On a listener, only apply those that you haven't set - for (map::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x) - { - if (!mycfg.parameters.count(x->first)) - mycfg.parameters[x->first] = x->second; - } + if (!CheckFilterCompat((mycfg), cfg)) + return false; } HLOGC(cnlog.Debug, @@ -5495,11 +5492,21 @@ SRT_REJECT_REASON CUDT::setupCC() // At this point we state everything is checked and the appropriate // corrector type is already selected, so now create it. HLOGC(pflog.Debug, log << "filter: Configuring: " << m_config.sPacketFilterConfig.c_str()); - if (!m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_config.sPacketFilterConfig.str())) + bool status = true; + try { - return SRT_REJ_FILTER; + // The filter configurer is build the way that allows to quit immediately + // exit by exception, but the exception is meant for the filter only. + status = m_PacketFilter.configure(this, &(m_pRcvQueue->m_UnitQueue), m_config.sPacketFilterConfig.str()); + } + catch (CUDTException& ) + { + status = false; } + if (!status) + return SRT_REJ_FILTER; + m_PktFilterRexmitLevel = m_PacketFilter.arqLevel(); } else diff --git a/srtcore/core.h b/srtcore/core.h index 9b0af91b9..ef395fd2f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -169,6 +169,7 @@ class CUDT friend class PacketFilter; friend class CUDTGroup; friend struct FByOldestActive; // this functional will use private fields + friend class TestMockCUDT; typedef srt::sync::steady_clock::time_point time_point; typedef srt::sync::steady_clock::duration duration; @@ -413,7 +414,7 @@ class CUDT void skipIncoming(int32_t seq); // For SRT_tsbpdLoop - CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop + static CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop std::set& pollset() { return m_sPollID; } CSrtConfig m_config; diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 598bdd0e0..c02af2821 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "packetfilter.h" #include "core.h" @@ -34,6 +35,93 @@ using namespace std; using namespace srt_logging; + +const char FECFilterBuiltin::defaultConfig [] = "fec,rows:1,layout:staircase,arq:onreq"; + +struct StringKeys +{ + string operator()(const pair item) + { + return item.first; + } +}; + +bool FECFilterBuiltin::verifyConfig(const SrtFilterConfig& cfg, string& w_error) +{ + string arspec = map_get(cfg.parameters, "layout"); + + if (arspec != "" && arspec != "even" && arspec != "staircase") + { + w_error = "value for 'layout' must be 'even' or 'staircase'"; + return false; + } + + string colspec = map_get(cfg.parameters, "cols"), rowspec = map_get(cfg.parameters, "rows"); + + int out_rows = 1; + + if (colspec != "") + { + int out_cols = atoi(colspec.c_str()); + if (out_cols < 2) + { + w_error = "at least 'cols' must be specified and > 1"; + return false; + } + } + + if (rowspec != "") + { + out_rows = atoi(rowspec.c_str()); + if (out_rows >= -1 && out_rows < 1) + { + w_error = "'rows' must be >=1 or negative < -1"; + return false; + } + } + + // Extra interpret level, if found, default never. + // Check only those that are managed. + string level = map_get(cfg.parameters, "arq"); + if (level != "") + { + static const char* const levelnames [] = {"never", "onreq", "always"}; + size_t i = 0; + for (i = 0; i < Size(levelnames); ++i) + { + if (strcmp(level.c_str(), levelnames[i]) == 0) + break; + } + + if (i == Size(levelnames)) + { + w_error = "'arq' value '" + level + "' invalid. Allowed: never, onreq, always"; + return false; + } + } + + set keys; + transform(cfg.parameters.begin(), cfg.parameters.end(), inserter(keys, keys.begin()), StringKeys()); + + // Delete all default parameters + SrtFilterConfig defconf; + ParseFilterConfig(defaultConfig, (defconf)); + for (map::const_iterator i = defconf.parameters.begin(); + i != defconf.parameters.end(); ++i) + keys.erase(i->first); + + // Delete mandatory parameters + keys.erase("cols"); + + if (!keys.empty()) + { + w_error = "Extra parameters. Allowed only: cols, rows, layout, arq"; + return false; + } + + return true; +} + FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector &provided, const string &confstr) : SrtPacketFilterBase(init) , m_fallback_level(SRT_ARQ_ONREQ) @@ -43,6 +131,13 @@ FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector if (!ParseFilterConfig(confstr, cfg)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + string ermsg; + if (!verifyConfig(cfg, (ermsg))) + { + LOGC(pflog.Error, log << "IPE: Filter config failed: " << ermsg); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + // Configuration supported: // - row only (number_rows == 1) // - columns only, no row FEC/CTL (number_rows < -1) @@ -57,33 +152,23 @@ FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector string shorter = arspec.size() > 5 ? arspec.substr(0, 5) : arspec; if (shorter == "even") m_arrangement_staircase = false; - else if (shorter != "" && shorter != "stair") - { - LOGC(pflog.Error, log << "FILTER/FEC: CONFIG: value for 'layout' must be 'even' or 'staircase'"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } string colspec = map_get(cfg.parameters, "cols"), rowspec = map_get(cfg.parameters, "rows"); - int out_rows = 1; - int out_cols = atoi(colspec.c_str()); - - if (colspec == "" || out_cols < 2) + if (colspec == "") { - LOGC(pflog.Error, log << "FILTER/FEC: CONFIG: at least 'cols' must be specified and > 1"); + LOGC(pflog.Error, log << "FEC filter config: parameter 'cols' is mandatory"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } + int out_rows = 1; + int out_cols = atoi(colspec.c_str()); + m_number_cols = out_cols; if (rowspec != "") { out_rows = atoi(rowspec.c_str()); - if (out_rows >= -1 && out_rows < 1) - { - LOGC(pflog.Error, log << "FILTER/FEC: CONFIG: 'rows' must be >=1 or negative < -1"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } } if (out_rows < 0) @@ -113,13 +198,10 @@ FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector break; } } + } - if (lv == -1) - { - LOGC(pflog.Error, log << "FILTER/FEC: CONFIG: 'arq': value '" << level << "' unknown"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - + if (lv != -1) + { m_fallback_level = SRT_ARQLevel(lv); } else @@ -127,7 +209,6 @@ FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector m_fallback_level = SRT_ARQ_ONREQ; } - // Required to store in the header when rebuilding rcv.id = socketID(); diff --git a/srtcore/fec.h b/srtcore/fec.h index ef150ce5d..9423f0d35 100644 --- a/srtcore/fec.h +++ b/srtcore/fec.h @@ -265,6 +265,9 @@ class FECFilterBuiltin: public SrtPacketFilterBase static const size_t EXTRA_SIZE = 4; virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; } + + static const char defaultConfig []; + static bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg); }; #endif diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 332acc22d..1122c06a2 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace srt_logging; using namespace srt::sync; -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config) +bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) { if (!SrtParseConfig(s, (w_config))) return false; @@ -35,12 +35,80 @@ bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config) if (!fac) return false; + if (ppf) + *ppf = fac; // Extract characteristic data w_config.extra_size = fac->ExtraSize(); return true; } +bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config) +{ + return ParseFilterConfig(s, (w_config), NULL); +} + +// Parameters are passed by value because they need to be potentially modicied inside. +bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) +{ + PacketFilter::Factory* fac = PacketFilter::find(w_agent.type); + if (!fac) + return false; + + SrtFilterConfig defaults; + if (!ParseFilterConfig(fac->defaultConfig(), (defaults))) + { + return false; + } + + set keys; + // Extract all keys to identify also unspecified parameters on both sides + // Note that theoretically for FEC it could simply check for the "cols" parameter + // that is the only mandatory one, but this is a procedure for packet filters in + // general and every filter may define its own set of parameters and mandatory rules. + for (map::iterator x = w_agent.parameters.begin(); x != w_agent.parameters.end(); ++x) + { + keys.insert(x->first); + if (peer.parameters.count(x->first) == 0) + peer.parameters[x->first] = x->second; + } + for (map::iterator x = peer.parameters.begin(); x != peer.parameters.end(); ++x) + { + keys.insert(x->first); + if (w_agent.parameters.count(x->first) == 0) + w_agent.parameters[x->first] = x->second; + } + + HLOGC(cnlog.Debug, log << "CheckFilterCompat: re-filled: AGENT:" << Printable(w_agent.parameters) + << " PEER:" << Printable(peer.parameters)); + + // Complete nonexistent keys with default values + for (map::iterator x = defaults.parameters.begin(); x != defaults.parameters.end(); ++x) + { + if (!w_agent.parameters.count(x->first)) + w_agent.parameters[x->first] = x->second; + if (!peer.parameters.count(x->first)) + peer.parameters[x->first] = x->second; + } + + for (set::iterator x = keys.begin(); x != keys.end(); ++x) + { + // Note: operator[] will insert an element with default value + // if it doesn't exist. This will inject the empty string as value, + // which is acceptable. + if (w_agent.parameters[*x] != peer.parameters[*x]) + { + LOGC(cnlog.Error, log << "Packet Filter (" << defaults.type << "): collision on '" << (*x) + << "' parameter (agent:" << w_agent.parameters[*x] << " peer:" << (peer.parameters[*x]) << ")"); + return false; + } + } + + // Mandatory parameters will be checked when trying to create the filter object. + + return true; +} + struct SortBySequence { bool operator()(const CUnit* u1, const CUnit* u2) @@ -232,7 +300,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co m_parent = parent; SrtFilterConfig cfg; - if (!ParseFilterConfig(confstr, cfg)) + if (!ParseFilterConfig(confstr, (cfg))) return false; // Extract the "type" key from parameters, or use diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index 13ec89b78..867aefcb3 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -16,32 +16,40 @@ #include #include "packet.h" -#include "queue.h" #include "utilities.h" #include "packetfilter_api.h" +class CUnitQueue; +class CUnit; +class CUDT; + class PacketFilter { friend class SrtPacketFilterBase; public: - typedef std::vector< std::pair > loss_seqs_t; typedef SrtPacketFilterBase* filter_create_t(const SrtFilterInitializer& init, std::vector&, const std::string& config); -private: - friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out); class Factory { public: virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, std::vector& provided, const std::string& confstr) = 0; // Characteristic data - virtual size_t ExtraSize() = 0; - + virtual size_t ExtraSize() const = 0; + + // Represent default parameters. This is for completing and comparing + // filter configurations from both parties. Possible values to return: + // - an empty string (all parameters are mandatory) + // - a form of: ",:,..." + virtual std::string defaultConfig() const = 0; + virtual bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg) const = 0; virtual ~Factory(); }; +private: + friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); template class Creator: public Factory @@ -52,7 +60,12 @@ class PacketFilter { return new Target(init, provided, confstr); } // Import the extra size data - virtual size_t ExtraSize() ATR_OVERRIDE { return Target::EXTRA_SIZE; } + virtual size_t ExtraSize() const ATR_OVERRIDE { return Target::EXTRA_SIZE; } + virtual std::string defaultConfig() const ATR_OVERRIDE { return Target::defaultConfig; } + virtual bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg) const ATR_OVERRIDE + { + return Target::verifyConfig(config, (w_errormsg)); + } public: Creator() {} @@ -192,6 +205,7 @@ class PacketFilter std::vector m_provided; }; +bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer); inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); } inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); } diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 07ce3392d..74279f9e3 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -79,7 +79,7 @@ struct SrtPacket }; -bool ParseFilterConfig(std::string s, SrtFilterConfig& out); +bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config); class SrtPacketFilterBase diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index e3bf296c1..61400df22 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -493,6 +493,9 @@ CSndQueue::~CSndQueue() delete m_pSndUList; } +int CSndQueue::ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } +int CSndQueue::sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } + #if ENABLE_LOGGING int CSndQueue::m_counter = 0; #endif diff --git a/srtcore/queue.h b/srtcore/queue.h index 6a3ba65f6..2d27c67b9 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -54,7 +54,6 @@ modified by #ifndef INC_SRT_QUEUE_H #define INC_SRT_QUEUE_H -#include "channel.h" #include "common.h" #include "packet.h" #include "socketconfig.h" @@ -66,6 +65,7 @@ modified by #include class CUDT; +class CChannel; struct CUnit { @@ -393,8 +393,8 @@ friend class CUDTUnited; bool getBind(char* dst, size_t len) const; #endif - int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } - int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } + int ioctlQuery(int type) const; + int sockoptQuery(int level, int type) const; void setClosing() { diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index e21fbda31..5e474310e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -61,6 +61,7 @@ written by #include "packet.h" #include "handshake.h" #include "logger_defs.h" +#include "packetfilter.h" // SRT Version constants #define SRT_VERSION_UNK 0 @@ -1068,14 +1069,21 @@ struct CSrtConfigSetter std::string arg((const char*)optval, optlen); // Parse the configuration string prematurely SrtFilterConfig fc; - if (!ParseFilterConfig(arg, fc)) + PacketFilter::Factory* fax = 0; + if (!ParseFilterConfig(arg, (fc), (&fax))) { LOGC(aclog.Error, - log << "SRTO_FILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " + log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " "FILTERTYPE (" << fc.type << ") must be installed (or builtin)"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } + std::string error; + if (!fax->verifyConfig(fc, (error))) + { + LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect config: " << error); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; if (co.zExpPayloadSize > efc_max_payload_size) diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index e2200f226..e289b384b 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "gtest/gtest.h" #include "packet.h" @@ -7,6 +9,9 @@ #include "packetfilter.h" #include "packetfilter_api.h" +// For direct imp access +#include "api.h" + using namespace std; class TestFECRebuilding: public testing::Test @@ -86,6 +91,617 @@ class TestFECRebuilding: public testing::Test } }; +class TestMockCUDT +{ +public: + CUDT* core; + + bool checkApplyFilterConfig(const string& s) + { + return core->checkApplyFilterConfig(s); + } +}; + +// The expected whole procedure of connection using FEC is +// expected to: +// +// 1. Successfully set the FEC option for correct filter type. +// - STOP ON FAILURE: unknown filter type (the table below, case D) +// 2. Perform the connection and integrate configurations. +// - STOP on failed integration (the table below, cases A and B) +// 3. Deliver on both sides identical configurations consisting +// of combined configurations and completed with default values. +// - Not possible if stopped before. +// +// Test coverage for the above cases: +// +// Success cases in all of the above: ConfigExchange, Connection, ConnectionReorder +// Failure cases: +// 1. ConfigExchangeFaux - setting unknown filter type +// 2. ConfigExchangeFaux, RejectionConflict, RejectionIncomplete, RejectionIncompleteEmpty +// +// For config exchange we have several possibilities here: +// +// - any same parameters with different values are rejected (Case A) +// - resulting configuiration should have the `cols` value set (Cases B) +// +// The configuration API rules that control correctness: +// +// 1. The first word defines an existing filter type. +// 2. Parameters are defined in whatever order. +// 3. Some parameters are optional and have default values. Others are mandatory. +// 4. A parameter provided twice remains with the last specification. +// 5. A parameter with empty value is like not provided parameter. +// 6. Only parameters handled by given filter type are allowed. +// 7. Every parameter may have limitations on the provided value: +// a. Numeric values in appropriate range +// b. String-enumeration with only certain values allowed +// +// Additionally there are rules for configuration integration: +// +// 8. Configuration consists of parameters provided in both sides. +// 9. Parameters lacking after integration are set to default values. +// 10. Parameters specified on both sides (including type) must be equal. +// 11. Empty configuration blindly accepts the configuration from the peer. +// 12. The final configuration must provide mandatory parameters +// +// Restrictive rules type are: 1, 6, 7, 10 +// +// Case description: +// A: Conflicting values on the same parameter (rejection, rule 10 failure) +// B: Missing a mandatory parameter (rejection, rule 12 failure) +// C: Successful setting and combining parameters +// 1: rules (positive): 1, 3, 6, 7(part), 8, 9, 12 +// 2: rules (positive): 1, 2, 3, 6, 7(part), 9, 10, 12 +// 3,4: rules (positive): 1, 2, 3(all), 6, 7(all), 8, 10, 12 +// 5: rules (positive): 1, 3, 4, 5, 6, 7, 8, 9, 12 +// 6: rules (positive): 1, 3, 6, 7, 8, 11, 12 +// D: Unknown filter type (failed option, rule 1) +// E: Incorrect values of the parameters (failed option, rule 7) +// F: Unknown excessive parameters (failed option, rule 6) +// +// Case |Party A | Party B | Situation | Test coverage +//------|------------------------|--------------------|---------------------|--------------- +// A |fec,cols:10 | fec,cols:20 | Conflict | ConfigExchangeFaux, RejectionConflict +// B1 |fec,rows:10 | fec,arq:never | Missing `cols` | RejectionIncomplete +// B2 |fec,rows:10 | | Missing `cols` | RejectionIncompleteEmpty +// C1 |fec,cols:10,rows:10 | fec | OK | ConfigExchange, Connection +// C2 |fec,cols:10,rows:10 | fec,rows:10,cols:10| OK | ConnectionReorder +// C3 |FULL 1 (see below) | FULL 2 (see below) | OK | ConnectionFull1 +// C4 |FULL 3 (see below) | FULL 4 (see below) | OK | ConnectionFull2 +// C5 |fec,cols:,cols:10 | fec,cols:,rows:10 | OK | ConnectionMess +// C6 |fec,rows:20,cols:20 | | OK | ConnectionForced +// D |FEC,Cols:10 | (unimportant) | Option rejected | ConfigExchangeFaux +// E1 |fec,cols:-10 | (unimportant) | Option rejected | ConfigExchangeFaux +// E2 |fec,cols:10,rows:0 | (unimportant) | Option rejected | ConfigExchangeFaux +// E3 |fec,cols:10,rows:-1 | (unimportant) | Option rejected | ConfigExchangeFaux +// E4 |fec,cols:10,layout:x (*)| (unimportant) | Option rejected | ConfigExchangeFaux +// E5 |fec,cols:10,arq:x (*) | (unimportant) | Option rejected | ConfigExchangeFaux +// F |fec,cols:10,weight:2 | (unimportant) | Option rejected | ConfigExchangeFaux +// +// (*) Here is just an example of a longer string that surely is wrong for this parameter. +// +// The configurations for FULL (cases C3 and C4) are longer and use all possible +// values in different order: +// 1. fec,cols:10,rows:20,arq:never,layout:even +// 1. fec,layout:even,rows:20,cols:10,arq:never +// 1. fec,cols:10,rows:20,arq:always,layout:even +// 1. fec,layout:even,rows:20,cols:10,arq:always + + +bool filterConfigSame(const string& config1, const string& config2) +{ + vector config1_vector; + Split(config1, ',', back_inserter(config1_vector)); + sort(config1_vector.begin(), config1_vector.end()); + + vector config2_vector; + Split(config2, ',', back_inserter(config2_vector)); + sort(config2_vector.begin(), config2_vector.end()); + + return config1_vector == config2_vector; +} + +TEST(TestFEC, ConfigExchange) +{ + srt_startup(); + + CUDTSocket* s1; + + SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + + TestMockCUDT m1; + m1.core = &s1->core(); + + // Can't access the configuration storage without + // accessing the private fields, so let's use the official API + + char fec_config1 [] = "fec,cols:10,rows:10"; + + srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + + EXPECT_TRUE(m1.checkApplyFilterConfig("fec,cols:10,arq:never")); + + char fec_configback[200]; + int fec_configback_size = 200; + srt_getsockflag(sid1, SRTO_PACKETFILTER, fec_configback, &fec_configback_size); + + // Order of parameters may differ, so store everything in a vector and sort it. + + string exp_config = "fec,cols:10,rows:10,arq:never,layout:staircase"; + + EXPECT_TRUE(filterConfigSame(fec_configback, exp_config)); + srt_cleanup(); +} + +TEST(TestFEC, ConfigExchangeFaux) +{ + srt_startup(); + + CUDTSocket* s1; + + SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + + const char* fec_config_wrong [] = { + "FEC,Cols:20", // D: unknown filter + "fec,cols:-10", // E1: invalid value for cols + "fec,cols:10,rows:0", // E2: invalid value for rows + "fec,cols:10,rows:-1", // E3: invalid value for rows + "fec,cols:10,layout:stairwars", // E4: invalid value for layout + "fec,cols:10,arq:sometimes", // E5: invalid value for arq + "fec,cols:10,weight:2" // F: invalid parameter name + }; + + for (auto badconfig: fec_config_wrong) + { + ASSERT_EQ(srt_setsockflag(sid1, SRTO_PACKETFILTER, badconfig, strlen(badconfig)), -1); + } + + TestMockCUDT m1; + m1.core = &s1->core(); + + // Can't access the configuration storage without + // accessing the private fields, so let's use the official API + + char fec_config1 [] = "fec,cols:20,rows:10"; + + EXPECT_NE(srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + + cout << "(NOTE: expecting a failure message)\n"; + EXPECT_FALSE(m1.checkApplyFilterConfig("fec,cols:10,arq:never")); + + srt_cleanup(); +} + +TEST(TestFEC, Connection) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:10,rows:10"; + char fec_config2 [] = "fec,cols:10,arq:never"; + char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + string caller_config = result_config1; + string accept_config = result_config2; + EXPECT_EQ(caller_config, accept_config); + + EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final)); + EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final)); + + srt_cleanup(); +} + +TEST(TestFEC, ConnectionReorder) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:10,rows:10"; + char fec_config2 [] = "fec,rows:10,cols:10"; + char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + string caller_config = result_config1; + string accept_config = result_config2; + EXPECT_EQ(caller_config, accept_config); + + EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final)); + EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final)); + + srt_cleanup(); +} + +TEST(TestFEC, ConnectionFull1) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even"; + char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never"; + char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + string caller_config = result_config1; + string accept_config = result_config2; + EXPECT_EQ(caller_config, accept_config); + + EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final)); + EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final)); + + srt_cleanup(); +} +TEST(TestFEC, ConnectionFull2) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even"; + char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always"; + char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + string caller_config = result_config1; + string accept_config = result_config2; + EXPECT_EQ(caller_config, accept_config); + + EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final)); + EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final)); + + srt_cleanup(); +} + +TEST(TestFEC, ConnectionMess) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:,cols:10"; + char fec_config2 [] = "fec,cols:,rows:10"; + char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + string caller_config = result_config1; + string accept_config = result_config2; + EXPECT_EQ(caller_config, accept_config); + + EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final)); + EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final)); + + srt_cleanup(); +} + +TEST(TestFEC, ConnectionForced) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,rows:20,cols:20"; + char fec_config_final [] = "fec,cols:20,rows:20"; + + ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + SRTSOCKET la[] = { l }; + SRTSOCKET a = srt_accept_bond(la, 1, 1000); + EXPECT_NE(a, SRT_ERROR); + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + // Now that the connection is established, check negotiated config + + char result_config1[200]; + int result_config1_size = 200; + char result_config2[200]; + int result_config2_size = 200; + + srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); + srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + + EXPECT_TRUE(filterConfigSame(result_config1, fec_config_final)); + EXPECT_TRUE(filterConfigSame(result_config2, fec_config_final)); + + srt_cleanup(); +} + +TEST(TestFEC, RejectionConflict) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,cols:10,rows:10"; + char fec_config2 [] = "fec,cols:20,arq:never"; + + srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + EXPECT_EQ(connect_res.get(), SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); + + bool no = false; + // Set non-blocking so that srt_accept can return + // immediately with failure. Just to make sure that + // the connection is not about to be established, + // also on the listener side. + srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no); + sockaddr_in scl; + int sclen = sizeof scl; + EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR); + + srt_cleanup(); +} + +TEST(TestFEC, RejectionIncompleteEmpty) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,rows:10"; + + srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + EXPECT_EQ(connect_res.get(), SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); + + bool no = false; + // Set non-blocking so that srt_accept can return + // immediately with failure. Just to make sure that + // the connection is not about to be established, + // also on the listener side. + srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no); + sockaddr_in scl; + int sclen = sizeof scl; + EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR); + + srt_cleanup(); +} + + +TEST(TestFEC, RejectionIncomplete) +{ + srt_startup(); + + SRTSOCKET s = srt_create_socket(); + SRTSOCKET l = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(l, (sockaddr*)& sa, sizeof(sa)); + srt_listen(l, 1); + + char fec_config1 [] = "fec,rows:10"; + char fec_config2 [] = "fec,arq:never"; + + srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + + auto connect_res = std::async(std::launch::async, [&s, &sa]() { + return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); + }); + + EXPECT_EQ(connect_res.get(), SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); + + bool no = false; + // Set non-blocking so that srt_accept can return + // immediately with failure. Just to make sure that + // the connection is not about to be established, + // also on the listener side. + srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no); + sockaddr_in scl; + int sclen = sizeof scl; + EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR); + + srt_cleanup(); +} TEST_F(TestFECRebuilding, Prepare) { diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 274207c65..625b91601 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -719,16 +719,19 @@ void SrtCommon::Init(string host, int port, string path, map par, bool blocking_snd = false, blocking_rcv = false; int dropdelay = 0; int size_int = sizeof (int), size_int64 = sizeof (int64_t), size_bool = sizeof (bool); + char packetfilter[100] = ""; + int packetfilter_size = 100; srt_getsockflag(m_sock, SRTO_MAXBW, &bandwidth, &size_int64); srt_getsockflag(m_sock, SRTO_RCVLATENCY, &latency, &size_int); srt_getsockflag(m_sock, SRTO_RCVSYN, &blocking_rcv, &size_bool); srt_getsockflag(m_sock, SRTO_SNDSYN, &blocking_snd, &size_bool); srt_getsockflag(m_sock, SRTO_SNDDROPDELAY, &dropdelay, &size_int); + srt_getsockflag(m_sock, SRTO_PACKETFILTER, (packetfilter), (&packetfilter_size)); Verb() << "OPTIONS: maxbw=" << bandwidth << " rcvlatency=" << latency << boolalpha << " blocking{rcv=" << blocking_rcv << " snd=" << blocking_snd - << "} snddropdelay=" << dropdelay; + << "} snddropdelay=" << dropdelay << " packetfilter=" << packetfilter; } if (!m_blocking_mode) From 47e477b29f08f6d752346bec7a2217f3013a6a2b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 8 Mar 2021 12:10:32 +0100 Subject: [PATCH 084/790] [core] Unguarded access to epolldesc from group sender code (#1846) --- srtcore/epoll.cpp | 6 ++++++ srtcore/epoll.h | 12 ++++++++---- srtcore/group.cpp | 17 +++++++++-------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 9d2317c0d..c4c31782f 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -808,6 +808,12 @@ int CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut, boo return 0; } +bool CEPoll::empty(CEPollDesc& d) +{ + ScopedLock lg (m_EPollLock); + return d.watch_empty(); +} + int CEPoll::release(const int eid) { ScopedLock pg(m_EPollLock); diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 97fe22b5c..23b6c0065 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -60,7 +60,7 @@ modified by #include "udt.h" -struct CEPollDesc +class CEPollDesc { const int m_iID; // epoll ID @@ -143,8 +143,6 @@ struct CEPollDesc std::string DisplayEpollWatch(); #endif -private: - /// Sockets that are subscribed for events in this eid. ewatch_t m_USockWatchState; @@ -159,7 +157,10 @@ std::string DisplayEpollWatch(); enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); } -public: + // Only CEPoll class should have access to it. + // Guarding private access to the class is not necessary + // within the epoll module. + friend class CEPoll; CEPollDesc(int id, int localID) : m_iID(id) @@ -423,6 +424,9 @@ friend class CRendezvousQueue; /// @retval >=0 number of ready sockets (actually size of `st`) int swait(CEPollDesc& d, fmap_t& st, int64_t msTimeOut, bool report_by_exception = true); + /// Empty subscription check - for internal use only. + bool empty(CEPollDesc& d); + /// Reports which events are ready on the given socket. /// @param mp socket event map retirned by `swait` /// @param sock which socket to ask diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f5d537dc7..4dbdc23fd 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1387,7 +1387,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // at the connecting stage. CEPoll::fmap_t sready; - if (m_SndEpolld->watch_empty()) + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, @@ -3486,7 +3486,7 @@ void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets // at the connecting stage. CEPoll::fmap_t sready; - if (m_SndEpolld->watch_empty()) + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); @@ -3494,10 +3494,6 @@ void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets } else { - // Some sockets could have been closed in the meantime. - if (m_SndEpolld->watch_empty()) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - { InvertedLock ug(m_GroupLock); m_pGlobal->m_EPoll.swait( @@ -3510,6 +3506,11 @@ void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } + // Some sockets could have been closed in the meantime. + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); // sockets in EX: should be moved to w_wipeme. @@ -3633,7 +3634,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); - if (m_SndEpolld->watch_empty()) + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) { // wipeme wiped, pending sockets checked, it can only mean that // all sockets are broken. @@ -3669,7 +3670,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, RetryWaitBlocked: { // Some sockets could have been closed in the meantime. - if (m_SndEpolld->watch_empty()) + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) { HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); From 53cc7384e5ea04d14547f9ac167d7272011432d0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 8 Mar 2021 15:50:50 +0100 Subject: [PATCH 085/790] [build] Bump version to 1.4.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d251f5712..86a9a8146 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.4.2) +set (SRT_VERSION 1.4.3) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables From db3db782afefd8b5949ebed8aacc01429eaa6161 Mon Sep 17 00:00:00 2001 From: cg82616424 Date: Tue, 9 Mar 2021 19:27:15 +0800 Subject: [PATCH 086/790] [core] Fixed SRT_EPOLL_IN epoll event loss problem (#1843) --- srtcore/core.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index df40ef0d5..c2d4caedf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7554,13 +7554,27 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } else { - if (m_config.bSynRecving) { - // signal a waiting "recv" call if there is any data available - CSync::lock_signal(m_RecvDataCond, m_RecvLock); + UniqueLock rdlock (m_RecvLock); + CSync rdcond (m_RecvDataCond, rdlock); + if (m_config.bSynRecving) + { + // signal a waiting "recv" call if there is any data available + rdcond.signal_locked(rdlock); + } + // acknowledge any waiting epolls to read + // fix SRT_EPOLL_IN event loss but rcvbuffer still have dataļ¼š + // 1. user call receive/receivemessage(about line number:6482) + // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false + // 3. but if we do not do some lock work here, will cause some sync problems between threads: + // (1) user thread: call receive/receivemessage + // (2) user thread: read data + // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false + // (4) receive thread: receive data and set SRT_EPOLL_IN to true + // (5) user thread: set SRT_EPOLL_IN to false + // 4. so , m_RecvLock must be used here to protect epoll event + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } - // acknowledge any waiting epolls to read - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { From d1d351dc19628266fa5119e619c1e48a8e6179e0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 9 Mar 2021 12:29:26 +0100 Subject: [PATCH 087/790] [core] Fixed lock around ackmessage (#1849) Also fixed wrong log FA qualification basing on the call stack --- srtcore/core.cpp | 17 +++++++++++++---- srtcore/group.cpp | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c2d4caedf..219f98440 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6006,11 +6006,12 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) // from the API call directly. This should be extra verified, if that // changes in the future. // - // What's important is that the lock on GroupLock cannot be applied - // here, both because it might be applied already, and because the - // locks on the later lock ordered mutexes are already set. if (m_parent->m_GroupOf) { + // What's important is that the lock on GroupLock cannot be applied + // here, both because it might be applied already, that is, according + // to the condition defined at this function's header, it is applied + // under this condition. Hence ackMessage can be defined as 100% locked. m_parent->m_GroupOf->ackMessage(first_msgno); } #endif @@ -7741,7 +7742,15 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) ScopedLock glock (s_UDTUnited.m_GlobControlLock); if (m_parent->m_GroupOf) { - HLOGC(xtlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); + HLOGC(inlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); + + // Guard access to m_iSndAckedMsgNo field + // Note: This can't be done inside CUDTGroup::ackMessage + // because this function is also called from CUDT::checkNeedDrop + // called from CUDT::sendmsg2 called from CUDTGroup::send, which + // applies the lock on m_GroupLock already. + ScopedLock glk (*m_parent->m_GroupOf->exp_groupLock()); + // NOTE: ackMessage also accepts and ignores the trap representation // which is SRT_MSGNO_CONTROL. m_parent->m_GroupOf->ackMessage(msgno_at_last_acked_seq); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 4dbdc23fd..b13caad08 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4216,6 +4216,7 @@ int32_t CUDTGroup::addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& // Very first packet, just set the msgno. m_iSndAckedMsgNo = w_mc.msgno; m_iSndOldestMsgNo = w_mc.msgno; + HLOGC(gslog.Debug, log << "addMessageToBuffer: initial message no #" << w_mc.msgno); } else if (m_iSndOldestMsgNo != m_iSndAckedMsgNo) { @@ -4241,6 +4242,8 @@ int32_t CUDTGroup::addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& // Position at offset is not included m_iSndOldestMsgNo = m_iSndAckedMsgNo; + HLOGC(gslog.Debug, + log << "addMessageToBuffer: ... after: oldest #" << m_iSndOldestMsgNo); } m_SenderBuffer.resize(m_SenderBuffer.size() + 1); @@ -4338,6 +4341,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) return stat; } +// [[using locked(CUDTGroup::m_GroupLock)]]; void CUDTGroup::ackMessage(int32_t msgno) { // The message id could not be identified, skip. From 8209e5aa808e0f0eb7aa0baab52f007449735951 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Mar 2021 15:35:18 +0100 Subject: [PATCH 088/790] [docs] Moved SRTO_MININPUTBW in alphabetic order --- docs/APISocketOptions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index f6ca590e1..49e612449 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -211,7 +211,6 @@ The following table lists SRT socket options in alphabetical order. Option detai | [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | | [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | | [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | | [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | | [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | @@ -224,6 +223,7 @@ The following table lists SRT socket options in alphabetical order. Option detai | [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | +| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | | [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | @@ -524,7 +524,7 @@ and keep the default 25% value for `SRTO_OHEADBW`*. | `SRTO_MININPUTBW` | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | This option is effective only if both `SRTO_MAXBW` and `SRTO_INPUTBW` are set to 0. -It controls the minimum allowed value of the input butrate estimate. +It controls the minimum allowed value of the input bitrate estimate. See [`SRTO_INPUTBW`](#SRTO_INPUTBW). From a1bcd4a50c7df9a96fb7bb55d13e351876061e8b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 10 Mar 2021 12:19:35 +0100 Subject: [PATCH 089/790] [core] Fixed Windows build with latest pthreads --- srtcore/sync.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/sync.h b/srtcore/sync.h index aeb4e0663..85cb96047 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -754,7 +754,7 @@ class CThread template inline Stream& operator<<(Stream& str, const CThread::id& cid) { -#if defined(_WIN32) && defined(PTW32_VERSION) +#if defined(_WIN32) && (defined(PTW32_VERSION) || defined (__PTW32_VERSION)) // This is a version specific for pthread-win32 implementation // Here pthread_t type is a structure that is not convertible // to a number at all. From 2ef0c8d8a16f03f5ac2b99af355660cf3672bb11 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 12 Mar 2021 11:49:47 +0100 Subject: [PATCH 090/790] [core] Fixed wrong type prefix for CUnit announcement (#1860) --- srtcore/packetfilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index 867aefcb3..dac988102 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -20,7 +20,7 @@ #include "packetfilter_api.h" class CUnitQueue; -class CUnit; +struct CUnit; class CUDT; class PacketFilter From 2c254126cec1613e8d64d3a146a7bbd7f5cc87ba Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 15 Mar 2021 12:05:55 +0100 Subject: [PATCH 091/790] [build] Fixed CMP0057 build break with unit tests enabled (#1867) --- CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86a9a8146..6c37bdc41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1189,6 +1189,16 @@ endif() if (ENABLE_UNITTESTS AND ENABLE_CXX11) + + if (${CMAKE_VERSION} VERSION_LESS "3.10.0") + message(STATUS "VERSION < 3.10 -- adding test using the old method") + set (USE_OLD_ADD_METHOD 1) + else() + message(STATUS "VERSION > 3.10 -- using NEW POLICY for in_list operator") + cmake_policy(SET CMP0057 NEW) # Support the new IN_LIST operator. + endif() + + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) find_package(GTest 1.8) @@ -1217,13 +1227,12 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) ${PTHREAD_LIBRARY} ) - if (${CMAKE_VERSION} VERSION_LESS "3.10.0") + if (USE_OLD_ADD_METHOD) add_test( NAME test-srt COMMAND ${CMAKE_BINARY_DIR}/test-srt ) else() - cmake_policy(SET CMP0057 NEW) # Support the new IN_LIST operator. gtest_add_tests( test-srt "" From 4b6dba2c6c28dec2669edbf10d69c056b77c876b Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Fri, 12 Mar 2021 13:24:33 +0100 Subject: [PATCH 092/790] [docs] Updated the link to the RFC --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 350f89f1d..809a2d56f 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ As audio/video packets are streamed from a source to a destination device, SRT d * [Why SRT Was Created](docs/why-srt-was-created.md) * [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) * SRT Cookbook: [website](https://srtlab.github.io/srt-cookbook), [GitHub](https://github.com/SRTLab/srt-cookbook) -* SRT RFC: [txt](https://haivision.github.io/srt-rfc/draft-sharabayko-mops-srt.txt), [html](https://haivision.github.io/srt-rfc/draft-sharabayko-mops-srt.html), [GitHub](https://github.com/Haivision/srt-rfc) +* SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) * [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) * [Contributing](CONTRIBUTING.md) * [Developer's Guide](docs/DevelopersGuide.md) From 18e88897deee3c60608abdb692228bfe98594827 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Thu, 11 Mar 2021 15:47:16 +0100 Subject: [PATCH 093/790] [core] Restoring RTTVar from cached RTT after reconnection --- srtcore/core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 219f98440..8753c78c4 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -871,7 +871,6 @@ void CUDT::open() m_iRTT = 10 * COMM_SYN_INTERVAL_US; m_iRTTVar = m_iRTT >> 1; - // set minimum NAK and EXP timeout to 300ms m_tdMinNakInterval = milliseconds_from(300); m_tdMinExpInterval = milliseconds_from(300); @@ -4417,6 +4416,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; + m_iRTTVar = m_iRTT >> 1; m_iBandwidth = ib.m_iBandwidth; } @@ -5315,6 +5315,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; + m_iRTTVar = m_iRTT >> 1; m_iBandwidth = ib.m_iBandwidth; } From 3605f6fce3a4934b6beff9d37c1fd3fcda24aea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 9 Mar 2021 17:16:22 +0100 Subject: [PATCH 094/790] [core] Removed condition that blocks extending group HS extension in future --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 8753c78c4..35ee4c968 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2662,7 +2662,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // - When receiving HS response from the Responder, with its mirror group ID, so the agent // must put the group into his peer group data int32_t groupdata[GRPD_E_SIZE] = {}; - if (bytelen < GRPD_MIN_SIZE * GRPD_FIELD_SIZE || bytelen % GRPD_FIELD_SIZE || blocklen > GRPD_E_SIZE) + if (bytelen < GRPD_MIN_SIZE * GRPD_FIELD_SIZE || bytelen % GRPD_FIELD_SIZE) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << "PEER'S GROUP wrong size: " << (bytelen/GRPD_FIELD_SIZE)); From 2bcc219216257e337a56f810c7aaabc20f852182 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 17 Mar 2021 10:41:42 +0100 Subject: [PATCH 095/790] [core] Fixed incorrect setting streamid in internal config in HS (last 4 characters) (#1868) * Fixed also the length check condition in StringStorage * Fixed problem with private option derivation (CONCEPT). Added UT to test derivation case * Updated docs regarding the StreamID no applicable to the listener socket. * Added UT for StreamID Co-authored-by: maxsharabayko Co-authored-by: stevomatthews --- docs/APISocketOptions.md | 4 + srtcore/core.cpp | 204 ++++++++++++++++++++--------------- srtcore/socketconfig.h | 2 +- test/test_socket_options.cpp | 170 +++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+), 85 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 49e612449..1d37ad738 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -1442,6 +1442,10 @@ override the value from the other side resulting in an arbitrary winner. Also in this connection both peers are known to one another and both have equivalent roles in the connection. +- **IMPORTANT**: This option is not derived by the accepted socket from the listener +socket, and setting it on a listener socket (see `srt_listen` function) doesn't +influence anything. + [Return to list](#list-of-options) --- diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 35ee4c968..fa6b561ca 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -114,6 +114,105 @@ const int UDT::ERROR = CUDT::ERROR; 2[15..0]: TsbPD delay [0..60000] msec */ +extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { + SRTO_SNDSYN, + SRTO_RCVSYN, + SRTO_LINGER, + SRTO_SNDTIMEO, + SRTO_RCVTIMEO, + SRTO_MAXBW, + SRTO_INPUTBW, + SRTO_MININPUTBW, + SRTO_OHEADBW, + SRTO_SNDDROPDELAY, + SRTO_CONNTIMEO, + SRTO_DRIFTTRACER, + SRTO_LOSSMAXTTL +}; + +static const int32_t + SRTO_R_PREBIND = BIT(0), //< cannot be modified after srt_bind() + SRTO_R_PRE = BIT(1), //< cannot be modified after connection is established + SRTO_POST_SPEC = BIT(2); //< executes some action after setting the option + +struct SrtOptionAction +{ + int flags[SRTO_E_SIZE]; + std::map private_default; + SrtOptionAction() + { + // Set everything to 0 to clear all flags + // When an option isn't present here, it means that: + // * it is not settable, or + // * the option is POST (non-restricted) + // * it has no post-actions + // The post-action may be defined independently on restrictions. + memset(flags, 0, sizeof flags); + + flags[SRTO_MSS] = SRTO_R_PREBIND; + flags[SRTO_FC] = SRTO_R_PRE; + flags[SRTO_SNDBUF] = SRTO_R_PREBIND; + flags[SRTO_RCVBUF] = SRTO_R_PREBIND; + flags[SRTO_UDP_SNDBUF] = SRTO_R_PREBIND; + flags[SRTO_UDP_RCVBUF] = SRTO_R_PREBIND; + flags[SRTO_RENDEZVOUS] = SRTO_R_PRE; + flags[SRTO_REUSEADDR] = SRTO_R_PREBIND; + flags[SRTO_MAXBW] = SRTO_POST_SPEC; + flags[SRTO_SENDER] = SRTO_R_PRE; + flags[SRTO_TSBPDMODE] = SRTO_R_PRE; + flags[SRTO_LATENCY] = SRTO_R_PRE; + flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_MININPUTBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_PASSPHRASE] = SRTO_R_PRE; + flags[SRTO_PBKEYLEN] = SRTO_R_PRE; + flags[SRTO_IPTTL] = SRTO_R_PREBIND; + flags[SRTO_IPTOS] = SRTO_R_PREBIND; + flags[SRTO_TLPKTDROP] = SRTO_R_PRE; + flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; + flags[SRTO_NAKREPORT] = SRTO_R_PRE; + flags[SRTO_VERSION] = SRTO_R_PRE; + flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; + flags[SRTO_RCVLATENCY] = SRTO_R_PRE; + flags[SRTO_PEERLATENCY] = SRTO_R_PRE; + flags[SRTO_MINVERSION] = SRTO_R_PRE; + flags[SRTO_STREAMID] = SRTO_R_PRE; + flags[SRTO_CONGESTION] = SRTO_R_PRE; + flags[SRTO_MESSAGEAPI] = SRTO_R_PRE; + flags[SRTO_PAYLOADSIZE] = SRTO_R_PRE; + flags[SRTO_TRANSTYPE] = SRTO_R_PREBIND; + flags[SRTO_KMREFRESHRATE] = SRTO_R_PRE; + flags[SRTO_KMPREANNOUNCE] = SRTO_R_PRE; + flags[SRTO_ENFORCEDENCRYPTION] = SRTO_R_PRE; + flags[SRTO_IPV6ONLY] = SRTO_R_PREBIND; + flags[SRTO_PEERIDLETIMEO] = SRTO_R_PRE; +#ifdef SRT_ENABLE_BINDTODEVICE + flags[SRTO_BINDTODEVICE] = SRTO_R_PREBIND; +#endif +#if ENABLE_EXPERIMENTAL_BONDING + flags[SRTO_GROUPCONNECT] = SRTO_R_PRE; +#endif + flags[SRTO_PACKETFILTER] = SRTO_R_PRE; + flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE; + + // For "private" options (not derived from the listener + // socket by an accepted socket) provide below private_default + // to which these options will be reset after blindly + // copying the option object from the listener socket. + // Note that this option cannot have runtime-dependent + // default value, like options affected by SRTO_TRANSTYPE. + + // Options may be of different types, but this value should be only + // used as a source of the value. For example, in case of int64_t you'd + // have to place here a string of 8 characters. It should be copied + // always in the hardware order, as this is what will be directly + // passed to a setting function. + private_default[SRTO_STREAMID] = string(); + } +} +srt_options_action; + + void CUDT::construct() { m_pSndBuffer = NULL; @@ -192,6 +291,26 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) // into a separate class for easier copying. m_config = ancestor.m_config; + // Reset values that shall not be derived to default ones. + // These declarations should be consistent with SRTO_R_PRIVATE flag. + for (size_t i = 0; i < Size(srt_options_action.flags); ++i) + { + string* pdef = map_getp(srt_options_action.private_default, SRT_SOCKOPT(i)); + if (pdef) + { + try + { + // Ignore errors here - this is a development-time granted + // value, not user-provided value. + m_config.set(SRT_SOCKOPT(i), pdef->data(), pdef->size()); + } + catch (...) + { + LOGC(gglog.Error, log << "IPE: failed to set a declared default option!"); + } + } + } + m_SrtHsSide = ancestor.m_SrtHsSide; // actually it sets it to HSD_RESPONDER m_bTLPktDrop = ancestor.m_bTLPktDrop; m_iReorderTolerance = m_config.iMaxReorderTolerance; // Initialize with maximum value @@ -214,89 +333,6 @@ CUDT::~CUDT() delete m_pRNode; } -extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { - SRTO_SNDSYN, - SRTO_RCVSYN, - SRTO_LINGER, - SRTO_SNDTIMEO, - SRTO_RCVTIMEO, - SRTO_MAXBW, - SRTO_INPUTBW, - SRTO_MININPUTBW, - SRTO_OHEADBW, - SRTO_SNDDROPDELAY, - SRTO_CONNTIMEO, - SRTO_DRIFTTRACER, - SRTO_LOSSMAXTTL -}; - -static const int32_t - SRTO_R_PREBIND = BIT(0), - SRTO_R_PRE = BIT(1), - SRTO_POST_SPEC = BIT(2); - -struct SrtOptionAction -{ - int flags[SRTO_E_SIZE]; - SrtOptionAction() - { - // Set everything to 0 to clear all flags - // When an option isn't present here, it means that: - // * it is not settable, or - // * the option is POST (non-restricted) - // * it has no post-actions - // The post-action may be defined independently on restrictions. - memset(flags, 0, sizeof flags); - - flags[SRTO_MSS] = SRTO_R_PREBIND; - flags[SRTO_FC] = SRTO_R_PRE; - flags[SRTO_SNDBUF] = SRTO_R_PREBIND; - flags[SRTO_RCVBUF] = SRTO_R_PREBIND; - flags[SRTO_UDP_SNDBUF] = SRTO_R_PREBIND; - flags[SRTO_UDP_RCVBUF] = SRTO_R_PREBIND; - flags[SRTO_RENDEZVOUS] = SRTO_R_PRE; - flags[SRTO_REUSEADDR] = SRTO_R_PREBIND; - flags[SRTO_MAXBW] = SRTO_POST_SPEC; - flags[SRTO_SENDER] = SRTO_R_PRE; - flags[SRTO_TSBPDMODE] = SRTO_R_PRE; - flags[SRTO_LATENCY] = SRTO_R_PRE; - flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_MININPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_PASSPHRASE] = SRTO_R_PRE; - flags[SRTO_PBKEYLEN] = SRTO_R_PRE; - flags[SRTO_IPTTL] = SRTO_R_PREBIND; - flags[SRTO_IPTOS] = SRTO_R_PREBIND; - flags[SRTO_TLPKTDROP] = SRTO_R_PRE; - flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; - flags[SRTO_NAKREPORT] = SRTO_R_PRE; - flags[SRTO_VERSION] = SRTO_R_PRE; - flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; - flags[SRTO_RCVLATENCY] = SRTO_R_PRE; - flags[SRTO_PEERLATENCY] = SRTO_R_PRE; - flags[SRTO_MINVERSION] = SRTO_R_PRE; - flags[SRTO_STREAMID] = SRTO_R_PRE; - flags[SRTO_CONGESTION] = SRTO_R_PRE; - flags[SRTO_MESSAGEAPI] = SRTO_R_PRE; - flags[SRTO_PAYLOADSIZE] = SRTO_R_PRE; - flags[SRTO_TRANSTYPE] = SRTO_R_PREBIND; - flags[SRTO_KMREFRESHRATE] = SRTO_R_PRE; - flags[SRTO_KMPREANNOUNCE] = SRTO_R_PRE; - flags[SRTO_ENFORCEDENCRYPTION] = SRTO_R_PRE; - flags[SRTO_IPV6ONLY] = SRTO_R_PREBIND; - flags[SRTO_PEERIDLETIMEO] = SRTO_R_PRE; -#ifdef SRT_ENABLE_BINDTODEVICE - flags[SRTO_BINDTODEVICE] = SRTO_R_PREBIND; -#endif -#if ENABLE_EXPERIMENTAL_BONDING - flags[SRTO_GROUPCONNECT] = SRTO_R_PRE; -#endif - flags[SRTO_PACKETFILTER] = SRTO_R_PRE; - flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE; - } -} -srt_options_action; - void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) { if (m_bBroken || m_bClosing) @@ -2570,7 +2606,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, // Un-swap on big endian machines ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); - m_config.sStreamName.set(target, bytelen); + m_config.sStreamName.set(target, strlen(target)); HLOGC(cnlog.Debug, log << "CONNECTOR'S REQUESTED SID [" << m_config.sStreamName.c_str() << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")"); diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 5e474310e..c41bf4caf 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -141,7 +141,7 @@ class StringStorage bool set(const char* s, size_t length) { - if (length >= SIZE) + if (length > SIZE) return false; memcpy(stor, s, length); diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index a94d18eec..74a607838 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -14,6 +14,8 @@ #include #include +// SRT includes +#include #include "srt.h" using namespace std; @@ -246,3 +248,171 @@ TEST_F(TestSocketOptions, MinInputBWRuntime) ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); } +TEST_F(TestSocketOptions, StreamIDWrongLen) +{ + char buffer[CSrtConfig::MAX_SID_LENGTH + 135]; + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a' + i % 25; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, buffer, CSrtConfig::MAX_SID_LENGTH+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. +TEST_F(TestSocketOptions, StreamIDOdd) +{ + // 13 characters, that is, 3*4+1 + string sid_odd = "something1234"; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_odd.c_str(), sid_odd.size()), SRT_SUCCESS); + + char buffer[CSrtConfig::MAX_SID_LENGTH + 135]; + int buffer_len = sizeof buffer; + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(std::string(buffer), sid_odd); + EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(strlen(buffer), sid_odd.size()); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a'; + buffer_len = (int)(sizeof buffer); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(strlen(buffer), sid_odd.size()); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + + +TEST_F(TestSocketOptions, StreamIDEven) +{ + // 12 characters = 4*3, that is, aligned to 4 + string sid_even = "123412341234"; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_even.c_str(), sid_even.size()), SRT_SUCCESS); + + char buffer[CSrtConfig::MAX_SID_LENGTH + 135]; + int buffer_len = sizeof buffer; + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(std::string(buffer), sid_even); + EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(strlen(buffer), sid_even.size()); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a'; + buffer_len = (int)(sizeof buffer); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(strlen(buffer), sid_even.size()); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + + +TEST_F(TestSocketOptions, StreamIDAlmostFull) +{ + // 12 characters = 4*3, that is, aligned to 4 + string sid_amost_full; + for (size_t i = 0; i < CSrtConfig::MAX_SID_LENGTH-2; ++i) + sid_amost_full += 'x'; + + // Just to manipulate the last ones. + size_t size = sid_amost_full.size(); + sid_amost_full[size-2] = 'y'; + sid_amost_full[size-1] = 'z'; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_amost_full.c_str(), sid_amost_full.size()), SRT_SUCCESS); + + char buffer[CSrtConfig::MAX_SID_LENGTH + 135]; + int buffer_len = sizeof buffer; + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(std::string(buffer), sid_amost_full); + EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(strlen(buffer), sid_amost_full.size()); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a'; + buffer_len = (int)(sizeof buffer); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(strlen(buffer), sid_amost_full.size()); + EXPECT_EQ(buffer[sid_amost_full.size()-1], 'z'); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +TEST_F(TestSocketOptions, StreamIDFull) +{ + // 12 characters = 4*3, that is, aligned to 4 + string sid_full; + for (size_t i = 0; i < CSrtConfig::MAX_SID_LENGTH; ++i) + sid_full += 'x'; + + // Just to manipulate the last ones. + size_t size = sid_full.size(); + sid_full[size-2] = 'y'; + sid_full[size-1] = 'z'; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_full.c_str(), sid_full.size()), SRT_SUCCESS); + + char buffer[CSrtConfig::MAX_SID_LENGTH + 135]; + int buffer_len = sizeof buffer; + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(std::string(buffer), sid_full); + EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(strlen(buffer), sid_full.size()); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a'; + buffer_len = (int)(sizeof buffer); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(strlen(buffer), sid_full.size()); + EXPECT_EQ(buffer[sid_full.size()-1], 'z'); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +TEST_F(TestSocketOptions, StreamIDLenListener) +{ + string stream_id_13 = "something1234"; + + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_STREAMID, stream_id_13.c_str(), stream_id_13.size()), SRT_SUCCESS); + + char buffer[648]; + int buffer_len = sizeof buffer; + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (SRTSOCKET sock : { m_caller_sock, accepted_sock }) + { + for (size_t i = 0; i < sizeof buffer; ++i) + buffer[i] = 'a'; + buffer_len = (int)(sizeof buffer); + EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "LISTENER"); + } + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} From 36fc3617220542e664aeff6de01b697b56b5a9f0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 17 Mar 2021 11:51:33 +0100 Subject: [PATCH 096/790] [docs] Updated and fixed blocking and error information (#1856) Co-authored-by: stevomatthews --- docs/API-functions.md | 73 +++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index bb08be06f..2f3b030b8 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -553,6 +553,12 @@ member connections are added or broken within the group, you can obtain this information through [`srt_group_data`](#srt_group_data) or the data filled by [`srt_sendmsg2`](#srt_sendmsg) and [`srt_recvmsg2`](#srt_recvmsg2). +If the `lsn` listener socket is configured for blocking mode +([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) set to true, default), +the call will block until the incoming connection is ready. Otherwise, the +call always returns immediately. The `SRT_EPOLL_IN` epoll event should be +checked on the `lsn` socket prior to calling this function in that case. + If the pending connection is a group connection (initiated on the peer side by calling the connection function using a group ID, and permitted on the listener socket by the [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) @@ -575,8 +581,8 @@ internal use only. | [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `addrlen`, when `addr` is not NULL | | [`SRT_EINVSOCK`](#srt_einvsock) | `lsn` designates no valid socket ID. | | [`SRT_ENOLISTEN`](#srt_enolisten) | `lsn` is not set up as a listener ([`srt_listen`](#srt_listen) not called). | -| [`SRT_EASYNCRCV`](#srt_easyncrcv) | No connection reported so far. This error is reported only when the `lsn` listener socket was
configured as non-blocking for reading ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) set to false); otherwise the call blocks
until a connection is reported or an error occurs | -| [`SRT_ESCLOSED`](#srt_esclosed) | The `lsn` socket has been closed while the function was blocking the call (if [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN)
is set to default true). This includes a situation when the socket was closed just at the
moment when a connection was made and the socket got closed during processing | +| [`SRT_EASYNCRCV`](#srt_easyncrcv) | No connection reported so far. This error is reported only in the non-blocking mode | +| [`SRT_ESCLOSED`](#srt_esclosed) | The `lsn` socket has been closed while the function was blocking the call. Including when the socket was closed just at the
moment when a connection was made (i.e., the socket got closed during processing) | | | | @@ -743,17 +749,27 @@ Connects a socket or a group to a remote party with a specified address and port * `namelen`: size of the object passed by `name` **NOTES:** - -1. The socket used here may be bound from upside (or binding and connection can -be done in one function, [`srt_connect_bind`](#srt_connect_bind)) so that it uses -a predefined network interface or local outgoing port. If not, it behaves as if -it was bound to `INADDR_ANY` (which binds on all interfaces) and port 0 (which -makes the system assign the port automatically). -2. When [`u`](#u) is a group, then this call can be done multiple times, each time + +1. The socket used here may be [bound by `srt_bind`](#srt_bind) before connecting, +or binding and connection can be done in one function ([`srt_connect_bind`](#srt_connect_bind)), +such that it uses a predefined network interface or local outgoing port. This is optional +in the case of a caller-listener arrangement, but obligatory for a rendezvous arrangement. +If not used, the binding will be done automatically to `INADDR_ANY` (which binds on all +interfaces) and port 0 (which makes the system assign the port automatically). + +2. This function is used for both connecting to the listening peer in a caller-listener +arrangement, and calling the peer in rendezvous mode. For the latter, the +[`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) flag must be set +to true prior to calling this function, and binding, as described in #1, +is in this case obligatory (see `SRT_ERDVUNBOUND` below). + +3. When [`u`](#u) is a group, then this call can be done multiple times, each time for another member connection, and a new member SRT socket will be created automatically for every call of this function. -3. If you want to connect a group to multiple links at once and use blocking + +4. If you want to connect a group to multiple links at once and use blocking mode, you might want to use [`srt_connect_group`](#srt_connect_group) instead. +This function also allows you to use additional settings, available only for groups. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -765,28 +781,31 @@ mode, you might want to use [`srt_connect_group`](#srt_connect_group) instead. | Errors | | |:------------------------------------- |:----------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | -| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Socket [`u`](#u) has set [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) to true, but [`srt_bind`](#srt_bind) hasn't yet been called on it.
The [`srt_connect`](#srt_connect) function is also used to connect a rendezvous socket, but rendezvous sockets
must be explicitly bound to a local interface prior to connecting. Non-rendezvous sockets (caller
sockets) can be left without binding - the call to [`srt_connect`](#srt_connect) will bind them automatically. | +| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Socket [`u`](#u) is in rendezvous mode, but it wasn't bound (see note #2) | | [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | | [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | | [`SRT_ENOSERVER`](#srt_enoserver) | Connection has been timed out (see [`SRTO_CONNTIMEO`](../docs/APISocketOptions.md#SRTO_CONNTIMEO)) | -| [`SRT_ESCLOSED`](#srt_esclosed) | The socket [`u`](#u) has been closed while the function was blocking the call (if [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to
default true) | +| [`SRT_ESCLOSED`](#srt_esclosed) | The socket [`u`](#u) has been closed while the function was blocking the call | | | | - -When the [`SRT_ECONNREJ`](#srt_econnrej) error is reported, you can get the reason -for a rejected connection from [`srt_getrejectreason`](#srt_getrejectreason). In -non-blocking mode (when [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) -is set to false), only [`SRT_EINVSOCK`](#srt_einvsock), [`SRT_ERDVUNBOUND`](#srt_erdvunbound) -and [`SRT_ECONNSOCK`](#srt_econnsock) can be reported. In all other cases the function -returns immediately with a success, and the only way to obtain the connecting status -is through the epoll flag with [`SRT_EPOLL_ERR`](#SRT_EPOLL_ERR). In this case you can -also call [`srt_getrejectreason`](#srt_getrejectreason) to get the detailed reason for -the error, including connection timeout ([`SRT_REJ_TIMEOUT`](#SRT_REJ_TIMEOUT)). - -Note that in case of failure the socket is in `SRTS_CONNECTING`, not in -`SRTS_BROKEN` state. After the failure was reported and you read any extra -information from the socket, the socket should be manually closed using -`srt_close` function. +If the `u` socket is configured for blocking mode (when +[`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to true, default), +the call will block until the connection succeeds or fails. The "early" errors +[`SRT_EINVSOCK`](#srt_einvsock), [`SRT_ERDVUNBOUND`](#srt_erdvunbound) and +[`SRT_ECONNSOCK`](#srt_econnsock) are reported in both modes immediately. Other +errors are "late" failures and can only be reported in blocking mode. + +In non-blocking mode, a successful connection can be recognized by the +`SRT_EPOLL_OUT` epoll event flag and a "late" failure by the `SRT_EPOLL_ERR` +flag. Note that the socket state in the case of a failed connection remains +`SRTS_CONNECTING` in that case. + +In the case of "late" failures you can additionally call +[`srt_getrejectreason`](#srt_getrejectreason) to get detailed error +information. Note that in blocking mode only for the `SRT_ECONNREJ` error +this function may return any additional information. In non-blocking +mode a detailed "late" failure cannot be distinguished, and therefore it +can also be obtained from this function. From 00e42d78f4be1ca13ed2bde8206baa291002f003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 8 Mar 2021 11:02:17 +0100 Subject: [PATCH 097/790] [core] Fixed incorrect group data sync on first connected socket --- srtcore/api.cpp | 11 ++++++----- srtcore/api.h | 1 + srtcore/core.cpp | 14 +++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5239693fc..f2bc0c709 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1605,11 +1605,6 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar break; } - if (was_empty) - { - g.syncWithSocket(ns->core(), HSD_INITIATOR); - } - HLOGC(aclog.Debug, log << "groupConnect: @" << sid << " connection successful, setting group OPEN (was " << (g.m_bOpened ? "ALREADY" : "NOT") << "), will " << (block_new_opened ? "" : "NOT ") << "block the connect call, status:" << SockStatusStr(st)); @@ -1911,6 +1906,12 @@ void CUDTUnited::deleteGroup(CUDTGroup* g) using srt_logging::gmlog; srt::sync::ScopedLock cg (m_GlobControlLock); + return deleteGroup_LOCKED(g); +} + +// [[using locked(m_GlobControlLock)]] +void CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) +{ SRT_ASSERT(g->groupEmpty()); // After that the group is no longer findable by GroupKeeper diff --git a/srtcore/api.h b/srtcore/api.h index 478b0955c..35f215ca3 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -305,6 +305,7 @@ friend class CRendezvousQueue; } void deleteGroup(CUDTGroup* g); + void deleteGroup_LOCKED(CUDTGroup* g); // [[using locked(m_GlobControlLock)]] CUDTGroup* findPeerGroup_LOCKED(SRTSOCKET peergroup) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fa6b561ca..e91763f9d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2953,6 +2953,9 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } + // Group existence is guarded, so we can now lock the group as well. + ScopedLock gl(*pg->exp_groupLock()); + // Now we know the group exists, but it might still be closed if (pg->closing()) { @@ -2967,14 +2970,19 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN // This is the first connection within this group, so this group // has just been informed about the peer membership. Accept it. pg->set_peerid(grpid); - HLOGC(cnlog.Debug, log << "HS/RSP: group $" << pg->id() << " mapped to peer mirror $" << pg->peerid()); + HLOGC(cnlog.Debug, log << "HS/RSP: group $" << pg->id() << " -> peer $" << pg->peerid() << ", copying characteristic data"); + + // The call to syncWithSocket is copying + // some interesting data from the first connected + // socket. This should be only done for the first successful connection. + pg->syncWithSocket(*this, HSD_INITIATOR); } // Otherwise the peer id must be the same as existing, otherwise // this group is considered already bound to another peer group. // (Note that the peer group is peer-specific, and peer id numbers // may repeat among sockets connected to groups established on // different peers). - else if (pg->peerid() != grpid) + else if (peer != grpid) { LOGC(cnlog.Error, log << "IPE: HS/RSP: group membership responded for peer $" << grpid << " but the current socket's group $" << pg->id() << " has already a peer $" << peer); @@ -3076,7 +3084,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. - s_UDTUnited.deleteGroup(gp); + s_UDTUnited.deleteGroup_LOCKED(gp); return -1; } From b38bb7f507201bbd8b20faee6bbfb4859dcaac92 Mon Sep 17 00:00:00 2001 From: quink-black Date: Thu, 18 Mar 2021 20:16:22 +0800 Subject: [PATCH 098/790] [core] Changed SRTO_CONNTIMEO restriction to PRE (#1864) * Changed SRTO_CONNTIMEO to be pre-restricted * Added timeout check to TestConnectionTimeout.BlockingLoop with 200ms tolerance. GitHub CI (macOS) gave up to 141 ms extra delay. Co-authored-by: Maxim Sharabayko --- docs/APISocketOptions.md | 4 ++-- srtcore/core.cpp | 2 +- srtcore/core.h | 2 +- test/test_connection_timeout.cpp | 12 ++++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/APISocketOptions.md b/docs/APISocketOptions.md index 1d37ad738..149241a73 100644 --- a/docs/APISocketOptions.md +++ b/docs/APISocketOptions.md @@ -202,7 +202,7 @@ The following table lists SRT socket options in alphabetical order. Option detai | :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| | [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | | [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | -| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | post | `int32_t` | ms | 3000 | 0.. | W | GSD+ | +| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | | [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | | [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | | [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | @@ -311,7 +311,7 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_CONNTIMEO` | 1.1.2 | post | `int32_t` | msec | 3000 | 0.. | W | GSD+ | +| `SRTO_CONNTIMEO` | 1.1.2 | pre | `int32_t` | msec | 3000 | 0.. | W | GSD+ | Connect timeout. This option applies to the caller and rendezvous connection modes. For the rendezvous mode (see `SRTO_RENDEZVOUS`) the effective connection timeout diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e91763f9d..da4f07668 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -125,7 +125,6 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_MININPUTBW, SRTO_OHEADBW, SRTO_SNDDROPDELAY, - SRTO_CONNTIMEO, SRTO_DRIFTTRACER, SRTO_LOSSMAXTTL }; @@ -172,6 +171,7 @@ struct SrtOptionAction flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; flags[SRTO_NAKREPORT] = SRTO_R_PRE; flags[SRTO_VERSION] = SRTO_R_PRE; + flags[SRTO_CONNTIMEO] = SRTO_R_PRE; flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; flags[SRTO_RCVLATENCY] = SRTO_R_PRE; flags[SRTO_PEERLATENCY] = SRTO_R_PRE; diff --git a/srtcore/core.h b/srtcore/core.h index ef395fd2f..0a4c2d86d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -116,7 +116,7 @@ enum AckDataItem }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); -static const size_t SRT_SOCKOPT_NPOST = 13; +static const size_t SRT_SOCKOPT_NPOST = 12; extern const SRT_SOCKOPT srt_post_opt_list []; enum GroupDataItem diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index bbd87e145..8b9bd31f8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -126,7 +126,6 @@ TEST_F(TestConnectionTimeout, Nonblocking) { int wlen = 2; SRTSOCKET write[2]; - using namespace std; const chrono::steady_clock::time_point chrono_ts_start = chrono::steady_clock::now(); // Here we check the connection timeout. @@ -143,9 +142,8 @@ TEST_F(TestConnectionTimeout, Nonblocking) { const chrono::steady_clock::time_point chrono_ts_end = chrono::steady_clock::now(); const auto delta_ms = chrono::duration_cast(chrono_ts_end - chrono_ts_start).count(); // Confidence interval border : +/-80 ms - EXPECT_LE(delta_ms, connection_timeout_ms + 80); - EXPECT_GE(delta_ms, connection_timeout_ms - 80); - cerr << "Timeout was: " << delta_ms << "\n"; + EXPECT_LE(delta_ms, connection_timeout_ms + 80) << "Timeout was: " << delta_ms; + EXPECT_GE(delta_ms, connection_timeout_ms - 80) << "Timeout was: " << delta_ms; EXPECT_EQ(rlen, 1); EXPECT_EQ(read[0], client_sock); @@ -186,8 +184,14 @@ TEST_F(TestConnectionTimeout, BlockingLoop) const sockaddr* psa = reinterpret_cast(&m_sa); for (int i = 0; i < 10; ++i) { + const chrono::steady_clock::time_point chrono_ts_start = chrono::steady_clock::now(); EXPECT_EQ(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR); + const auto delta_ms = chrono::duration_cast(chrono::steady_clock::now() - chrono_ts_start).count(); + // Confidence interval border : +/-200 ms + EXPECT_LE(delta_ms, connection_timeout_ms + 200) << "Timeout was: " << delta_ms; + EXPECT_GE(delta_ms, connection_timeout_ms - 200) << "Timeout was: " << delta_ms; + const int error_code = srt_getlasterror(nullptr); EXPECT_EQ(error_code, SRT_ENOSERVER); if (error_code != SRT_ENOSERVER) From 449d917aeb3ab10afc338679268b39cd48705062 Mon Sep 17 00:00:00 2001 From: quink-black Date: Fri, 19 Mar 2021 17:47:01 +0800 Subject: [PATCH 099/790] [core] Fix getopt SRTO_TLPKTDROP (#1865) Convert to bool instead of int32_t --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index da4f07668..df20a08ee 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -606,8 +606,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_TLPKTDROP: - *(int32_t *)optval = m_bTLPktDrop; - optlen = sizeof(int32_t); + *(bool *)optval = m_bTLPktDrop; + optlen = sizeof(bool); break; case SRTO_SNDDROPDELAY: From 2d418692451a3e47c67357ab9ca9b812e5775291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 22 Mar 2021 10:45:36 +0100 Subject: [PATCH 100/790] [core](minor) Use correct keyword for CEPollDesc --- srtcore/group.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/group.h b/srtcore/group.h index afb05f0f9..4f014a849 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -637,9 +637,9 @@ class CUDTGroup bool m_bTLPktDrop; int64_t m_iTsbPdDelay_us; int m_RcvEID; - struct CEPollDesc* m_RcvEpolld; + class CEPollDesc* m_RcvEpolld; int m_SndEID; - struct CEPollDesc* m_SndEpolld; + class CEPollDesc* m_SndEpolld; int m_iSndTimeOut; // sending timeout in milliseconds int m_iRcvTimeOut; // receiving timeout in milliseconds From 631a976a707ba24f32eef9bf85771eb72a08fab5 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 23 Mar 2021 11:46:48 +0100 Subject: [PATCH 101/790] [apps] Fixed wrong handling of no transmission in the test apps (#1881) Added timeout for UDP reading with interrupt support --- apps/apputil.hpp | 2 ++ testing/testmedia.cpp | 28 +++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 773b96b36..6031668ba 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -68,8 +68,10 @@ inline void SysCleanupNetwork() {} #ifdef _WIN32 inline int SysError() { return ::GetLastError(); } +const int SysAGAIN = WSAEWOULDBLOCK; #else inline int SysError() { return errno; } +const int SysAGAIN = EAGAIN; #endif sockaddr_any CreateAddr(const std::string& name, unsigned short port = 0, int pref_family = AF_UNSPEC); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 625b91601..1cb49cb64 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -2296,12 +2296,19 @@ MediaPacket SrtSource::Read(size_t chunk) ::transmit_throw_on_interrupt = true; stat = srt_recvmsg2(m_sock, data.data(), chunk, &mctrl); ::transmit_throw_on_interrupt = false; - if (stat == SRT_ERROR) + if (stat != SRT_ERROR) + { + ready = true; + } + else { + int syserr = 0; + int err = srt_getlasterror(&syserr); + if (!m_blocking_mode) { // EAGAIN for SRT READING - if (srt_getlasterror(NULL) == SRT_EASYNCRCV) + if (err == SRT_EASYNCRCV) { Epoll_again: Verb() << "AGAIN: - waiting for data by epoll(" << srt_epoll << ")..."; @@ -2347,8 +2354,11 @@ MediaPacket SrtSource::Read(size_t chunk) { // In blocking mode it uses a minimum of 1s timeout, // and continues only if interrupt not requested. - if (srt_getlasterror(NULL) == SRT_EASYNCRCV) + if (!::transmit_int_state && (err == SRT_EASYNCRCV || err == SRT_ETIMEOUT)) + { + ready = false; continue; + } } Error("srt_recvmsg2"); } @@ -2890,6 +2900,11 @@ class UdpSource: public virtual Source, public virtual UdpCommon if (stat == -1) Error(SysError(), "Binding address for UDP"); eof = false; + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + Error(SysError(), "Setting timeout for UDP"); } MediaPacket Read(size_t chunk) override @@ -2897,13 +2912,20 @@ class UdpSource: public virtual Source, public virtual UdpCommon bytevector data(chunk); sockaddr_any sa(sadr.family()); int64_t srctime = 0; +AGAIN: int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen()); + int err = SysError(); if (transmit_use_sourcetime) { srctime = srt_time_now(); } if (stat == -1) + { + if (!::transmit_int_state && err == SysAGAIN) + goto AGAIN; + Error(SysError(), "UDP Read/recvfrom"); + } if (stat < 1) { From 22d5736ed49b58b35f490642274848e868dc863a Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 23 Mar 2021 17:01:26 +0100 Subject: [PATCH 102/790] [core] Added restrictions on pktseq/msgno fields in srt_sendmsg2 call (#1879) --- srtcore/core.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index df20a08ee..0d2f1e6e0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6288,10 +6288,17 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) IF_HEAVY_LOGGING(steady_clock::time_point ts_srctime = steady_clock::time_point() + microseconds_from(w_mctrl.srctime)); +#if ENABLE_EXPERIMENTAL_BONDING // Check if seqno has been set, in case when this is a group sender. // If the sequence is from the past towards the "next sequence", // simply return the size, pretending that it has been sent. - if (w_mctrl.pktseq != SRT_SEQNO_NONE && m_iSndNextSeqNo != SRT_SEQNO_NONE) + + // NOTE: it's assumed that if this is a group member, then + // an attempt to call srt_sendmsg2 has been rejected, and so + // the pktseq field has been set by the internal group sender function. + if (m_parent->m_GroupOf + && w_mctrl.pktseq != SRT_SEQNO_NONE + && m_iSndNextSeqNo != SRT_SEQNO_NONE) { if (CSeqNo::seqcmp(w_mctrl.pktseq, seqno) < 0) { @@ -6300,6 +6307,7 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) return size; } } +#endif // Set this predicted next sequence to the control information. // It's the sequence of the FIRST (!) packet from all packets used to send From 93de9c843ea49aea1f172633c9d51098311aa304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 23 Mar 2021 18:30:13 +0100 Subject: [PATCH 103/790] [core] Removed unnecessary lock that could cause a deadlock --- srtcore/core.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0d2f1e6e0..c31b7088a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1668,7 +1668,6 @@ bool CUDT::createSrtHandshake( if (have_group) { // NOTE: See information about mutex ordering in api.h - ScopedLock grd (m_parent->m_ControlLock); // Required to make sure ScopedLock gdrg (s_UDTUnited.m_GlobControlLock); if (!m_parent->m_GroupOf) { From 1d4338a0c8b0c2b6be4a21236bc254b5eec3e847 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 24 Mar 2021 11:21:55 +0100 Subject: [PATCH 104/790] [core] Added SRT_SYNC_CLOCK_TYPE preprocessor definition (#1885) --- docs/API-functions.md | 14 +++++++++----- srtcore/api.cpp | 2 ++ srtcore/sync.h | 41 ++++++++++++++++++++++++++++++++++++--- srtcore/sync_posix.cpp | 44 ++++++++++++++++++++++-------------------- 4 files changed, 72 insertions(+), 29 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index 2f3b030b8..e00e2e74f 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -2492,7 +2492,7 @@ the sending to a stream with a handler function that will receive them. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) ---- +--- ### srt_setlogflags @@ -2513,6 +2513,9 @@ The following flags are available, as collected in the `logging_api.h` public he - `SRT_LOGF_DISABLE_SEVERITY`: Do not provide severity information in the header - `SRT_LOGF_DISABLE_EOL`: Do not add the end-of-line character to the log line +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- ## Time Access @@ -2578,7 +2581,6 @@ although it's highly recommended to use one of the above monotonic clocks, as system clock is vulnerable to time modifications during transmission. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -2624,9 +2626,11 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [statistics.md](statistics | | | ---- +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- - ## Diagnostics +## Diagnostics General notes concerning the `getlasterror` diagnostic functions: when an API function ends up with error, this error information is stored in a thread-local @@ -3221,7 +3225,7 @@ The operation timed out. This can happen if you have a timeout set by an option extra argument ([`srt_epoll_wait`](#srt_epoll_wait) or [`srt_accept_bond`](#srt_accept_bond)) and the function call was blocking, but the required timeout time has passed. - + #### `SRT_ECONGEST` **NOTE**: This error is used only in an experimental version that requires diff --git a/srtcore/api.cpp b/srtcore/api.cpp index f2bc0c709..4293d8021 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -287,6 +287,8 @@ int CUDTUnited::startup() m_bGCStatus = true; + HLOGC(inlog.Debug, log << "SRT Clock Type: " << SRT_SYNC_CLOCK_STR); + return 0; } diff --git a/srtcore/sync.h b/srtcore/sync.h index 85cb96047..271b15426 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -11,8 +11,15 @@ #ifndef INC_SRT_SYNC_H #define INC_SRT_SYNC_H -//#define ENABLE_STDCXX_SYNC -//#define ENABLE_CXX17 +// Possible internal clock types +#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock +#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC +#define SRT_SYNC_CLOCK_WINQPC 2 +#define SRT_SYNC_CLOCK_MACH_ABSTIME 3 +#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4 +#define SRT_SYNC_CLOCK_AMD64_RDTSC 5 +#define SRT_SYNC_CLOCK_IA32_RDTSC 6 +#define SRT_SYNC_CLOCK_IA64_ITC 7 #include #include @@ -21,9 +28,37 @@ #include #include #include +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_STDCXX_STEADY +#define SRT_SYNC_CLOCK_STR "STDCXX_STEADY" #else #include + +// Defile clock type to use +#ifdef IA32 +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA32_RDTSC +#define SRT_SYNC_CLOCK_STR "IA32_RDTSC" +#elif defined(IA64) +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA64_ITC +#define SRT_SYNC_CLOCK_STR "IA64_ITC" +#elif defined(AMD64) +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_AMD64_RDTSC +#define SRT_SYNC_CLOCK_STR "AMD64_RDTSC" +#elif defined(_WIN32) +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_WINQPC +#define SRT_SYNC_CLOCK_STR "WINQPC" +#elif TARGET_OS_MAC +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_MACH_ABSTIME +#define SRT_SYNC_CLOCK_STR "MACH_ABSTIME" +#elif defined(ENABLE_MONOTONIC_CLOCK) +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_GETTIME_MONOTONIC +#define SRT_SYNC_CLOCK_STR "GETTIME_MONOTONIC" +#else +#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY +#define SRT_SYNC_CLOCK_STR "POSIX_GETTIMEOFDAY" #endif + +#endif // ENABLE_STDCXX_SYNC + #include "utilities.h" class CUDTException; // defined in common.h @@ -773,7 +808,7 @@ namespace this_thread #if !defined(_WIN32) usleep(count_microseconds(t)); // microseconds #else - Sleep(count_milliseconds(t)); + Sleep((DWORD) count_milliseconds(t)); #endif } } diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index d40e23713..56a89d6b9 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -21,14 +21,10 @@ #include "common.h" #if defined(_WIN32) -#define TIMING_USE_QPC #include "win/wintime.h" #include #elif TARGET_OS_MAC -#define TIMING_USE_MACH_ABS_TIME #include -#elif defined(ENABLE_MONOTONIC_CLOCK) -#define TIMING_USE_CLOCK_GETTIME #endif namespace srt_logging @@ -44,7 +40,7 @@ namespace sync void rdtsc(uint64_t& x) { -#ifdef IA32 +#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA32_RDTSC uint32_t lval, hval; // asm volatile ("push %eax; push %ebx; push %ecx; push %edx"); // asm volatile ("xor %eax, %eax; cpuid"); @@ -52,30 +48,32 @@ void rdtsc(uint64_t& x) // asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax"); x = hval; x = (x << 32) | lval; -#elif defined(IA64) +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA64_ITC asm("mov %0=ar.itc" : "=r"(x)::"memory"); -#elif defined(AMD64) +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_AMD64_RDTSC uint32_t lval, hval; asm("rdtsc" : "=a"(lval), "=d"(hval)); x = hval; x = (x << 32) | lval; -#elif defined(TIMING_USE_QPC) +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC // This function should not fail, because we checked the QPC // when calling to QueryPerformanceFrequency. If it failed, // the m_bUseMicroSecond was set to true. QueryPerformanceCounter((LARGE_INTEGER*)&x); -#elif defined(TIMING_USE_MACH_ABS_TIME) +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_MACH_ABSTIME x = mach_absolute_time(); -#elif defined(TIMING_USE_CLOCK_GETTIME) +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC // get_cpu_frequency() returns 1 us accuracy in this case timespec tm; clock_gettime(CLOCK_MONOTONIC, &tm); x = tm.tv_sec * uint64_t(1000000) + (tm.tv_nsec / 1000); -#else +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY // use system call to read time clock for other archs timeval t; gettimeofday(&t, 0); x = t.tv_sec * uint64_t(1000000) + t.tv_usec; +#else +#error Wrong SRT_SYNC_CLOCK #endif } @@ -83,21 +81,25 @@ int64_t get_cpu_frequency() { int64_t frequency = 1; // 1 tick per microsecond. -#if defined(TIMING_USE_QPC) +#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC LARGE_INTEGER ccf; // in counts per second if (QueryPerformanceFrequency(&ccf)) + { frequency = ccf.QuadPart / 1000000; // counts per microsecond + } + else + { + // Can't throw an exception, it won't be handled. + LOGC(inlog.Error, log << "IPE: QueryPerformanceFrequency failed with " << GetLastError()); + } -#elif defined(TIMING_USE_CLOCK_GETTIME) - frequency = 1; - -#elif defined(TIMING_USE_MACH_ABS_TIME) - +#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_MACH_ABSTIME mach_timebase_info_data_t info; mach_timebase_info(&info); frequency = info.denom * int64_t(1000) / info.numer; -#elif defined(IA32) || defined(IA64) || defined(AMD64) +#elif SRT_SYNC_CLOCK >= SRT_SYNC_CLOCK_AMD64_RDTSC && SRT_SYNC_CLOCK <= SRT_SYNC_CLOCK_IA64_ITC + // SRT_SYNC_CLOCK_AMD64_RDTSC or SRT_SYNC_CLOCK_IA32_RDTSC or SRT_SYNC_CLOCK_IA64_ITC uint64_t t1, t2; rdtsc(t1); @@ -287,7 +289,7 @@ Condition::~Condition() {} void Condition::init() { pthread_condattr_t* attr = NULL; -#if ENABLE_MONOTONIC_CLOCK +#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC pthread_condattr_t CondAttribs; pthread_condattr_init(&CondAttribs); pthread_condattr_setclock(&CondAttribs, CLOCK_MONOTONIC); @@ -311,7 +313,7 @@ void Condition::wait(UniqueLock& lock) bool Condition::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time) { timespec timeout; -#if ENABLE_MONOTONIC_CLOCK +#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &timeout); const uint64_t now_us = timeout.tv_sec * uint64_t(1000000) + (timeout.tv_nsec / 1000); #else @@ -422,7 +424,7 @@ void srt::sync::CThread::join() #ifdef HEAVY_LOGGING else { - LOGC(inlog.Debug, log << "pthread_join SUCCEEDED"); + HLOGC(inlog.Debug, log << "pthread_join SUCCEEDED"); } #endif // After joining, joinable should be false From 5a4683962b390e5f60bb3ca7437c505999e8dded Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Mon, 29 Mar 2021 13:34:26 +0200 Subject: [PATCH 105/790] [core] Extended logs for negative or zero RTT estimate on the receiver side (#1876) * Minor refactoring to processCtrl function, UMSG_ACKACK * Minor formatting in CACKWindow::acknowledge function * The time of ACKACK reception is now passed as an argument to the CACKWindow::acknowledge function --- srtcore/core.cpp | 33 +++++++-- srtcore/core.h | 170 ++++++++++++++++++++++----------------------- srtcore/window.cpp | 26 +++---- srtcore/window.h | 62 +++++++++-------- 4 files changed, 157 insertions(+), 134 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c31b7088a..5a1c62209 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8198,19 +8198,40 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement { int32_t ack = 0; - const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack); + + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, currtime); + + if (rtt == -1) + { + if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) + { + LOGC(inlog.Warn, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + break; + } + + LOGC(inlog.Error, + log << CONID() << "IPE: ACK record not found, can't estimate RTT " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + break; + } + if (rtt <= 0) { LOGC(inlog.Error, - log << CONID() << "IPE: ACK node overwritten when acknowledging " << ctrlpkt.getAckSeqNo() - << " (ack extracted: " << ack << ")"); + log << CONID() << "IPE: invalid RTT estimate " << rtt + << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); break; } - // if increasing delay detected... + // If increasing delay is detected // sendCtrl(UMSG_CGWARNING); - // RTT EWMA + // Calculate RTT (EWMA) on the receiver side m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); m_iRTT = avg_iir<8>(m_iRTT, rtt); @@ -8240,7 +8261,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) #endif } - // update last ACK that has been received by the sender + // Update last ACK that has been received by the sender if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) m_iRcvLastAckAck = ack; diff --git a/srtcore/core.h b/srtcore/core.h index 0a4c2d86d..9ee3df6d7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -259,23 +259,22 @@ class CUDT } }; - static const SRTSOCKET INVALID_SOCK = -1; // invalid socket descriptor - static const int ERROR = -1; // socket api error returned value + static const SRTSOCKET INVALID_SOCK = -1; // Invalid socket descriptor + static const int ERROR = -1; // Socket api error returned value static const int HS_VERSION_UDT4 = 4; static const int HS_VERSION_SRT1 = 5; // Parameters // - // Note: use notation with X*1000*1000* ... instead of million zeros in a row. - // In C++17 there is a possible notation of 5'000'000 for convenience, but that's - // something only for a far future. - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; + // Note: use notation with X*1000*1000*... instead of million zeros in a row + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; int handshakeVersion() { @@ -295,37 +294,38 @@ class CUDT SRTSOCKET socketID() const { return m_SocketID; } - static CUDT* getUDTHandle(SRTSOCKET u); - static std::vector existingSockets(); + static CUDT* getUDTHandle(SRTSOCKET u); + static std::vector existingSockets(); void addressAndSend(CPacket& pkt); void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); - bool isOPT_TsbPd() const { return m_config.bTSBPD; } - int RTT() const { return m_iRTT; } - int RTTVar() const { return m_iRTTVar; } - int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } - int32_t schedSeqNo() const { return m_iSndNextSeqNo; } - bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } - srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } - - int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } - int flowWindowSize() const { return m_iFlowWindowSize; } - int32_t deliveryRate() const { return m_iDeliveryRate; } - int bandwidth() const { return m_iBandwidth; } - int64_t maxBandwidth() const { return m_config.llMaxBW; } - int MSS() const { return m_config.iMSS; } - - uint32_t peerLatency_us() const {return m_iPeerTsbPdDelay_ms * 1000; } - int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout; } - size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } - size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } - int sndLossLength() { return m_pSndLossList->getLossLength(); } - int32_t ISN() const { return m_iISN; } - int32_t peerISN() const { return m_iPeerISN; } - duration minNAKInterval() const { return m_tdMinNakInterval; } - sockaddr_any peerAddr() const { return m_PeerAddr; } + bool isOPT_TsbPd() const { return m_config.bTSBPD; } + int RTT() const { return m_iRTT; } + int RTTVar() const { return m_iRTTVar; } + int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } + int32_t schedSeqNo() const { return m_iSndNextSeqNo; } + bool overrideSndSeqNo(int32_t seq); + + srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } + srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } + + int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + int flowWindowSize() const { return m_iFlowWindowSize; } + int32_t deliveryRate() const { return m_iDeliveryRate; } + int bandwidth() const { return m_iBandwidth; } + int64_t maxBandwidth() const { return m_config.llMaxBW; } + int MSS() const { return m_config.iMSS; } + + uint32_t peerLatency_us() const { return m_iPeerTsbPdDelay_ms * 1000; } + int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout; } + size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } + size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } + int sndLossLength() { return m_pSndLossList->getLossLength(); } + int32_t ISN() const { return m_iISN; } + int32_t peerISN() const { return m_iPeerISN; } + duration minNAKInterval() const { return m_tdMinNakInterval; } + sockaddr_any peerAddr() const { return m_PeerAddr; } /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @param lastack is the sequence number of the first unacknowledged packet. @@ -691,28 +691,28 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - static CUDTUnited s_UDTUnited; // UDT global management base + static CUDTUnited s_UDTUnited; // UDT global management base private: // Identification - CUDTSocket* const m_parent; // temporary, until the CUDTSocket class is merged with CUDT - SRTSOCKET m_SocketID; // UDT socket number - SRTSOCKET m_PeerID; // peer id, for multiplexer + CUDTSocket* const m_parent; // Temporary, until the CUDTSocket class is merged with CUDT + SRTSOCKET m_SocketID; // UDT socket number + SRTSOCKET m_PeerID; // Peer ID, for multiplexer // HSv4 (legacy handshake) support) - time_point m_tsSndHsLastTime; //Last SRT handshake request time - int m_iSndHsRetryCnt; //SRT handshake retries left + time_point m_tsSndHsLastTime; // Last SRT handshake request time + int m_iSndHsRetryCnt; // SRT handshake retries left #if ENABLE_EXPERIMENTAL_BONDING - SRT_GROUP_TYPE m_HSGroupType; // group type about-to-be-set in the handshake + SRT_GROUP_TYPE m_HSGroupType; // Group type about-to-be-set in the handshake #endif private: - int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes - int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds - int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds - bool m_bTLPktDrop; // Enable Too-late Packet Drop - UniquePtr m_pCryptoControl; // congestion control SRT class (small data extension) - CCache* m_pCache; // network information cache + int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes + int m_iTsbPdDelay_ms; // Rx delay to absorb burst, in milliseconds + int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst, in milliseconds + bool m_bTLPktDrop; // Enable Too-late Packet Drop + UniquePtr m_pCryptoControl; // Congestion control SRT class (small data extension) + CCache* m_pCache; // Network information cache // Congestion control std::vector m_Slots[TEV_E_SIZE]; @@ -727,7 +727,7 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - volatile bool m_bListening; // If the UDT entit is listening to connection + volatile bool m_bListening; // If the UDT entity is listening to connection volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed volatile bool m_bConnected; // Whether the connection is on or off volatile bool m_bClosing; // If the UDT entity is closing @@ -736,7 +736,7 @@ class CUDT volatile bool m_bPeerHealth; // If the peer status is normal volatile int m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected + int m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second @@ -746,8 +746,8 @@ class CUDT int m_iByteDeliveryRate; // Byte arrival rate at the receiver side - CHandShake m_ConnReq; // connection request - CHandShake m_ConnRes; // connection response + CHandShake m_ConnReq; // Connection request + CHandShake m_ConnRes; // Connection response CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state HandshakeSide m_SrtHsSide; // HSv5 rendezvous handshake side resolved from cookie contest (DRAW if not yet resolved) @@ -758,32 +758,32 @@ class CUDT /*volatile*/ duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles - /*volatile*/ duration m_tdSendTimeDiff; // aggregate difference in inter-packet sending time + /*volatile*/ duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time volatile int m_iFlowWindowSize; // Flow control window size - volatile double m_dCongestionWindow; // congestion window size + volatile double m_dCongestionWindow; // Congestion window size private: // Timers - /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below - /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time - - /*volatile*/ duration m_tdACKInterval; // ACK interval - /*volatile*/ duration m_tdNAKInterval; // NAK interval - /*volatile*/ time_point m_tsLastRspTime; // time stamp of last response from the peer - /*volatile*/ time_point m_tsLastRspAckTime; // time stamp of last ACK from the peer - /*volatile*/ time_point m_tsLastSndTime; // time stamp of last data/ctrl sent (in system ticks) - time_point m_tsLastWarningTime; // Last time that a warning message is sent - time_point m_tsLastReqTime; // last time when a connection request is sent + /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below + /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time + + /*volatile*/ duration m_tdACKInterval; // ACK interval + /*volatile*/ duration m_tdNAKInterval; // NAK interval + /*volatile*/ time_point m_tsLastRspTime; // Timestamp of last response from the peer + /*volatile*/ time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer + /*volatile*/ time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) + time_point m_tsLastWarningTime; // Last time that a warning message is sent + time_point m_tsLastReqTime; // last time when a connection request is sent time_point m_tsRcvPeerStartTime; - time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) - time_point m_tsLastAckTime; // Timestamp of last ACK - duration m_tdMinNakInterval; // NAK timeout lower bound; too small value can cause unnecessary retransmission - duration m_tdMinExpInterval; // timeout lower bound threshold: too small timeout can cause problem + time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) + time_point m_tsLastAckTime; // Timestamp of last ACK + duration m_tdMinNakInterval; // NAK timeout lower bound; too small value can cause unnecessary retransmission + duration m_tdMinExpInterval; // Timeout lower bound threshold: too small timeout can cause problem - int m_iPktCount; // packet counter for ACK - int m_iLightACKCount; // light ACK counter + int m_iPktCount; // Packet counter for ACK + int m_iLightACKCount; // Light ACK counter - time_point m_tsNextSendTime; // scheduled time of next packet sending + time_point m_tsNextSendTime; // Scheduled time of next packet sending volatile int32_t m_iSndLastFullAck; // Last full ACK received volatile int32_t m_iSndLastAck; // Last ACK received @@ -843,17 +843,17 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data - CRcvBuffer* m_pRcvBuffer; //< Receiver buffer - CRcvLossList* m_pRcvLossList; //< Receiver loss list - std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. - int m_iReorderTolerance; //< Current value of dynamic reorder tolerance - int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_FreshLoss; // Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; // Current value of dynamic reorder tolerance + int m_iConsecEarlyDelivery; // Increases with every OOO packet that came m_ACKWindow; //< ACK history window - CPktTimeWindow<16, 64> m_RcvTimeWindow; //< Packet arrival time window + CACKWindow m_ACKWindow; // ACK history window + CPktTimeWindow<16, 64> m_RcvTimeWindow; // Packet arrival time window - int32_t m_iRcvLastAck; //< Last sent ACK + int32_t m_iRcvLastAck; // Last sent ACK #ifdef ENABLE_LOGGING int32_t m_iDebugPrevLastAck; #endif @@ -869,10 +869,10 @@ class CUDT uint32_t m_uPeerSrtFlags; bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets - bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead. + bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead srt::sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle - srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock. + srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent srt::sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining diff --git a/srtcore/window.cpp b/srtcore/window.cpp index 0c40af128..ea8386fe1 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -77,22 +77,21 @@ void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t s r_iTail = (r_iTail + 1) % size; } -int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack) +int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const steady_clock::time_point& currtime) { + // Head has not exceeded the physical boundary of the window if (r_iHead >= r_iTail) { - // Head has not exceeded the physical boundary of the window - for (int i = r_iTail, n = r_iHead; i < n; ++ i) { - // looking for identical ACK Seq. No. + // Looking for an identical ACK Seq. No. if (seq == r_aSeq[i].iACKSeqNo) { - // return the Data ACK it carried + // Return the Data ACK it carried r_ack = r_aSeq[i].iACK; - // calculate RTT - const int rtt = count_microseconds(steady_clock::now() - r_aSeq[i].tsTimeStamp); + // Calculate RTT estimate + const int rtt = count_microseconds(currtime - r_aSeq[i].tsTimeStamp); if (i + 1 == r_iHead) { @@ -106,22 +105,22 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 } } - // Bad input, the ACK node has been overwritten + // The record about ACK is not found in the buffer, RTT can not be calculated return -1; } // Head has exceeded the physical window boundary, so it is behind tail for (int j = r_iTail, n = r_iHead + size; j < n; ++ j) { - // looking for indentical ACK seq. no. + // Looking for an identical ACK Seq. No. if (seq == r_aSeq[j % size].iACKSeqNo) { - // return Data ACK + // Return the Data ACK it carried j %= size; r_ack = r_aSeq[j].iACK; - // calculate RTT - const int rtt = count_microseconds(steady_clock::now() - r_aSeq[j].tsTimeStamp); + // Calculate RTT estimate + const int rtt = count_microseconds(currtime - r_aSeq[j].tsTimeStamp); if (j == r_iHead) { @@ -135,9 +134,10 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 } } - // bad input, the ACK node has been overwritten + // The record about ACK is not found in the buffer, RTT can not be calculated return -1; } + } //////////////////////////////////////////////////////////////////////////////// diff --git a/srtcore/window.h b/srtcore/window.h index b3baf0d60..b7f510163 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -58,20 +58,20 @@ modified by #include #include #endif -#include "udt.h" #include "packet.h" +#include "udt.h" namespace ACKWindowTools { struct Seq { - int32_t iACKSeqNo; // Seq. No. for the ACK packet - int32_t iACK; // Data Seq. No. carried by the ACK packet - srt::sync::steady_clock::time_point tsTimeStamp; // The timestamp when the ACK was sent + int32_t iACKSeqNo; // Seq. No. of the ACK packet + int32_t iACK; // Data packet Seq. No. carried by the ACK packet + srt::sync::steady_clock::time_point tsTimeStamp; // The timestamp when the ACK was sent }; void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack); - int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack); + int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const srt::sync::steady_clock::time_point& currtime); } template @@ -89,22 +89,24 @@ class CACKWindow ~CACKWindow() {} /// Write an ACK record into the window. - /// @param [in] seq ACK seq. no. - /// @param [in] ack DATA ACK no. + /// @param [in] seq Seq. No. of the ACK packet + /// @param [in] ack Data packet Seq. No. carried by the ACK packet void store(int32_t seq, int32_t ack) { return ACKWindowTools::store(m_aSeq, SIZE, m_iHead, m_iTail, seq, ack); } - /// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT . - /// @param [in] seq ACK-2 seq. no. - /// @param [out] ack the DATA ACK no. that matches the ACK-2 no. - /// @return RTT. + /// Search the ACKACK "seq" in the window, find out the data packet "ack" + /// and calculate RTT estimate based on the ACK/ACKACK pair + /// @param [in] seq Seq. No. of the ACK packet carried within ACKACK + /// @param [out] ack Acknowledged data packet Seq. No. from the ACK packet that matches the ACKACK + /// @param [in] currtime The timestamp of ACKACK packet reception by the receiver + /// @return RTT - int acknowledge(int32_t seq, int32_t& r_ack) + int acknowledge(int32_t seq, int32_t& r_ack, const srt::sync::steady_clock::time_point& currtime) { - return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack); + return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack, currtime); } private: @@ -112,7 +114,7 @@ class CACKWindow typedef ACKWindowTools::Seq Seq; Seq m_aSeq[SIZE]; - int m_iHead; // Pointer to the lastest ACK record + int m_iHead; // Pointer to the latest ACK record int m_iTail; // Pointer to the oldest ACK record private: @@ -323,22 +325,22 @@ class CPktTimeWindow: CPktTimeWindowTools } private: - int m_aPktWindow[ASIZE]; // packet information window (inter-packet time) - int m_aBytesWindow[ASIZE]; // - int m_iPktWindowPtr; // position pointer of the packet info. window. - mutable srt::sync::Mutex m_lockPktWindow; // used to synchronize access to the packet window - - int m_aProbeWindow[PSIZE]; // record inter-packet time for probing packet pairs - int m_iProbeWindowPtr; // position pointer to the probing window - mutable srt::sync::Mutex m_lockProbeWindow; // used to synchronize access to the probe window - - int m_iLastSentTime; // last packet sending time - int m_iMinPktSndInt; // Minimum packet sending interval - - srt::sync::steady_clock::time_point m_tsLastArrTime; // last packet arrival time - srt::sync::steady_clock::time_point m_tsCurrArrTime; // current packet arrival time - srt::sync::steady_clock::time_point m_tsProbeTime; // arrival time of the first probing packet - int32_t m_Probe1Sequence; // sequence number for which the arrival time was notified + int m_aPktWindow[ASIZE]; // Packet information window (inter-packet time) + int m_aBytesWindow[ASIZE]; + int m_iPktWindowPtr; // Position pointer of the packet info. window + mutable srt::sync::Mutex m_lockPktWindow; // Used to synchronize access to the packet window + + int m_aProbeWindow[PSIZE]; // Record inter-packet time for probing packet pairs + int m_iProbeWindowPtr; // Position pointer to the probing window + mutable srt::sync::Mutex m_lockProbeWindow; // Used to synchronize access to the probe window + + int m_iLastSentTime; // Last packet sending time + int m_iMinPktSndInt; // Minimum packet sending interval + + srt::sync::steady_clock::time_point m_tsLastArrTime; // Last packet arrival time + srt::sync::steady_clock::time_point m_tsCurrArrTime; // Current packet arrival time + srt::sync::steady_clock::time_point m_tsProbeTime; // Arrival time of the first probing packet + int32_t m_Probe1Sequence; // Sequence number for which the arrival time was notified private: CPktTimeWindow(const CPktTimeWindow&); From 0e7185e84080ae40bab639226a36dc7510001506 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Mon, 29 Mar 2021 13:35:17 +0200 Subject: [PATCH 106/790] [docs] Moved API docs in a separate folder, improved API documentation (#1893) --- README.md | 13 +- docs/{ => API}/API-functions.md | 500 +++++++++--------- .../API-socket-options.md} | 31 +- docs/{ => API}/API.md | 83 ++- docs/{ => API}/statistics.md | 54 +- docs/AccessControl.md | 2 +- docs/README.md | 10 + docs/handshake.md | 2 +- 8 files changed, 342 insertions(+), 353 deletions(-) rename docs/{ => API}/API-functions.md (94%) rename docs/{APISocketOptions.md => API/API-socket-options.md} (98%) rename docs/{ => API}/API.md (93%) rename docs/{ => API}/statistics.md (93%) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 809a2d56f..eda49484e 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,15 @@ As audio/video packets are streamed from a source to a destination device, SRT d ### Guides -* [Why SRT Was Created](docs/why-srt-was-created.md) -* [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) -* SRT Cookbook: [website](https://srtlab.github.io/srt-cookbook), [GitHub](https://github.com/SRTLab/srt-cookbook) -* SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) +* [SRT API Documents](docs/API/) * [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) -* [Contributing](CONTRIBUTING.md) * [Developer's Guide](docs/DevelopersGuide.md) -* [SRT Encryption](docs/encryption.md) -* [API](docs/API.md) +* [Contributing](CONTRIBUTING.md) * [Reporting problems](docs/reporting.md) +* SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) +* SRT CookBook: [Website](https://srtlab.github.io/srt-cookbook), [GitHub Repo](https://github.com/SRTLab/srt-cookbook) +* [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) +* [Why SRT Was Created](docs/why-srt-was-created.md) ## Requirements diff --git a/docs/API-functions.md b/docs/API/API-functions.md similarity index 94% rename from docs/API-functions.md rename to docs/API/API-functions.md index e00e2e74f..9ed45dc7b 100644 --- a/docs/API-functions.md +++ b/docs/API/API-functions.md @@ -99,7 +99,7 @@ | [srt_bistats](#srt_bistats) | Reports the current statistics | | | | -

Asynchronous Operations (epoll)

+

Asynchronous Operations (Epoll)

| *Function / Structure* | *Description* | |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | @@ -166,8 +166,8 @@ | [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | Rendezvous cookie collision | | [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | Both parties have defined a passprhase for connection and they differ | | [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | Only one connection party has set up a password | -| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | The value for [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) flag is different on both connection parties | -| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | The [`SRTO_PACKETFILTER`](../docs/APISocketOptions.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | +| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | The value for [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties | +| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | The group type or some group settings are incompatible for both connection parties | | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | The connection wasn't rejected, but it timed out | | | | @@ -215,13 +215,13 @@ [`SRT_EASYNCSND`](#srt_easyncsnd) | Sending operation is not ready to perform | [`SRT_EASYNCRCV`](#srt_easyncrcv) | Receiving operation is not ready to perform | [`SRT_ETIMEOUT`](#srt_etimeout) | The operation timed out | -[`SRT_ECONGEST`](#srt_econgest) | With [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) set to true,
some packets were dropped by sender | +[`SRT_ECONGEST`](#srt_econgest) | With [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) set to true,
some packets were dropped by sender | [`SRT_EPEERERR`](#srt_epeererr) | Receiver peer is writing to a file that the agent is sending | | | | -## Library initialization +## Library Initialization * [srt_startup](#srt_startup) * [srt_cleanup](#srt_cleanup) @@ -275,7 +275,14 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne `srt_cleanup` function exactly the same number of times. -## Creating and configuring sockets +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + + +## Creating and Configuring Sockets * [srt_socket](#srt_socket) * [srt_create_socket](#srt_create_socket) @@ -303,9 +310,9 @@ is decided at the call of [`srt_connect`](#srt_connect) or [`srt_bind`](#srt_bin using `SOCK_STREAM` or `SOCK_DGRAM` symbols (with the latter being misleading, as the message mode has nothing to do with UDP datagrams and it's rather similar to the SCTP protocol). In SRT these two modes are available by setting -[`SRTO_TRANSTYPE`](../docs/APISocketOptions.md#SRTO_TRANSTYPE). The default is `SRTT_LIVE`. If, however, you set -[`SRTO_TRANSTYPE`](../docs/APISocketOptions.md#SRTO_TRANSTYPE) to `SRTT_FILE` for file mode, you can then leave the -[`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) option as false (default), which corresponds to "stream" mode +[`SRTO_TRANSTYPE`](API-socket-options.md#SRTO_TRANSTYPE). The default is `SRTT_LIVE`. If, however, you set +[`SRTO_TRANSTYPE`](API-socket-options.md#SRTO_TRANSTYPE) to `SRTT_FILE` for file mode, you can then leave the +[`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) option as false (default), which corresponds to "stream" mode (TCP-like), or set it to true, which corresponds to "message" mode (SCTP-like). @@ -356,14 +363,14 @@ This call is obligatory for a listening socket before calling [`srt_listen`](#sr and for rendezvous mode before calling [`srt_connect`](#srt_connect); otherwise it's optional. For a listening socket it defines the network interface and the port where the listener should expect a call request. In the case of rendezvous mode (when the -socket has set [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) to +socket has set [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) to true both parties connect to one another) it defines the network interface and port from which packets will be sent to the peer, and the port to which the peer is expected to send packets. For a connecting socket this call can set up the outgoing port to be used in the communication. It is allowed that multiple SRT sockets share one local outgoing -port, as long as [`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) +port, as long as [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to *true* (default). Without this call the port will be automatically selected by the system. @@ -385,8 +392,6 @@ connecting, use [`srt_connect_bind`](#srt_connect_bind) for that purpose. | | | - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -400,7 +405,6 @@ int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); A version of [`srt_bind`](#srt_bind) that acquires a given UDP socket instead of creating one. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -427,7 +431,6 @@ Gets the current status of the socket. Possible states are: | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -450,7 +453,6 @@ This function can be used for diagnostics. It is especially useful when the socket needs to be closed asynchronously. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -476,6 +478,12 @@ last user closed. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + ## Connecting @@ -503,7 +511,7 @@ socket and the [`srt_accept`](#srt_accept) function: * [`srt_listen_callback`](#srt_listen_callback) installs a user function that will be called before [`srt_accept`](#srt_accept) can happen -* [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) option allows +* [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) option allows the listener socket to accept group connections | Returns | | @@ -516,14 +524,13 @@ the listener socket to accept group connections | [`SRT_EINVPARAM`](#srt_einvparam) | Value of `backlog` is 0 or negative. | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid SRT socket. | | [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | [`srt_bind`](#srt_bind) has not yet been called on that socket. | -| [`SRT_ERDVNOSERV`](#srt_erdvnoserv) | [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) flag is set to true on specified socket. | +| [`SRT_ERDVNOSERV`](#srt_erdvnoserv) | [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) flag is set to true on specified socket. | | [`SRT_EINVOP`](#srt_einvop) | Internal error (should not happen when [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) is reported). | | [`SRT_ECONNSOCK`](#srt_econnsock) | The socket is already connected. | -| [`SRT_EDUPLISTEN`](#srt_eduplisten) | The address used in [`srt_bind`](#srt_bind) by this socket is already occupied by another listening socket.
Binding multiple sockets to one IP address and port is allowed, as long as
[`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) is set to true, but only one of these sockets can be set up as a listener. | +| [`SRT_EDUPLISTEN`](#srt_eduplisten) | The address used in [`srt_bind`](#srt_bind) by this socket is already occupied by another listening socket.
Binding multiple sockets to one IP address and port is allowed, as long as
[`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to true, but only one of these sockets can be set up as a listener. | | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -554,14 +561,14 @@ information through [`srt_group_data`](#srt_group_data) or the data filled by [`srt_sendmsg2`](#srt_sendmsg) and [`srt_recvmsg2`](#srt_recvmsg2). If the `lsn` listener socket is configured for blocking mode -([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) set to true, default), +([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) set to true, default), the call will block until the incoming connection is ready. Otherwise, the call always returns immediately. The `SRT_EPOLL_IN` epoll event should be checked on the `lsn` socket prior to calling this function in that case. If the pending connection is a group connection (initiated on the peer side by calling the connection function using a group ID, and permitted on the listener -socket by the [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) +socket by the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag), then the value returned is a group ID. This function then creates a new group, as well as a new socket for this connection, that will be added to the group. Once the group is created this way, further connections within the same @@ -586,7 +593,6 @@ internal use only. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -642,7 +648,6 @@ calling this function. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -687,13 +692,13 @@ The callback function gets the following parameters passed: * `ns`: The freshly created socket to handle the incoming connection * `hs_version`: The handshake version (usually 5, pre-1.3 versions of SRT use 4) * `peeraddr`: The address of the incoming connection -* `streamid`: The value set to [`SRTO_STREAMID`](../docs/APISocketOptions.md#SRTO_STREAMID) option set on the peer side +* `streamid`: The value set to [`SRTO_STREAMID`](API-socket-options.md#SRTO_STREAMID) option set on the peer side Note that SRT versions that use handshake version 4 are incapable of using any extensions, such as `streamid`. However they do support encryption. Note also that the SRT version isn't extracted at this point. However you can prevent connections with versions that are too old by using the -[`SRTO_MINVERSION`](../docs/APISocketOptions.md#SRTO_MINVERSION) option. +[`SRTO_MINVERSION`](API-socket-options.md#SRTO_MINVERSION) option. The callback function is given an opportunity to: @@ -728,7 +733,6 @@ Avoid any extensive search operations. It is best to cache in memory whatever database you have to check against the data received in `streamid` or `peeraddr`. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -759,7 +763,7 @@ interfaces) and port 0 (which makes the system assign the port automatically). 2. This function is used for both connecting to the listening peer in a caller-listener arrangement, and calling the peer in rendezvous mode. For the latter, the -[`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) flag must be set +[`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) flag must be set to true prior to calling this function, and binding, as described in #1, is in this case obligatory (see `SRT_ERDVUNBOUND` below). @@ -784,12 +788,12 @@ This function also allows you to use additional settings, available only for gro | [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Socket [`u`](#u) is in rendezvous mode, but it wasn't bound (see note #2) | | [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | | [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | -| [`SRT_ENOSERVER`](#srt_enoserver) | Connection has been timed out (see [`SRTO_CONNTIMEO`](../docs/APISocketOptions.md#SRTO_CONNTIMEO)) | +| [`SRT_ENOSERVER`](#srt_enoserver) | Connection has been timed out (see [`SRTO_CONNTIMEO`](API-socket-options.md#SRTO_CONNTIMEO)) | | [`SRT_ESCLOSED`](#srt_esclosed) | The socket [`u`](#u) has been closed while the function was blocking the call | | | | If the `u` socket is configured for blocking mode (when -[`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to true, default), +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, default), the call will block until the connection succeeds or fails. The "early" errors [`SRT_EINVSOCK`](#srt_einvsock), [`SRT_ERDVUNBOUND`](#srt_erdvunbound) and [`SRT_ECONNSOCK`](#srt_econnsock) are reported in both modes immediately. Other @@ -808,7 +812,6 @@ mode a detailed "late" failure cannot be distinguished, and therefore it can also be obtained from this function. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -856,7 +859,6 @@ different families (that is, both `source` and `target` must be `AF_INET` or `AF_INET6`), although you may mix links over IPv4 and IPv6 in one group. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -873,7 +875,6 @@ specifying the Initial Sequence Number for data transmission. Normally this valu is generated randomly. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -884,7 +885,7 @@ int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_nam const struct sockaddr* remote_name, int remote_namelen); ``` Performs a rendezvous connection. This is a shortcut for doing bind locally, -setting the [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) option +setting the [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) option to true, and doing [`srt_connect`](#srt_connect). **Arguments**: @@ -913,7 +914,6 @@ to true, and doing [`srt_connect`](#srt_connect). allowed (that is, both `local_name` and `remote_name` must be `AF_INET` or `AF_INET6`). - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -927,7 +927,7 @@ This call installs a callback hook, which will be executed on a given [`u`](#u) socket or all member sockets of a [`u`](#u) group, just after a pending connection in the background has been resolved and the connection has failed. Note that this function is not guaranteed to be called if the [`u`](#u) socket is set to blocking -mode ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) option set to true). +mode ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) option set to true). It is guaranteed to be called when a socket is in non-blocking mode, or when you use a group. @@ -982,28 +982,20 @@ typedef void srt_connect_callback_fn(void* opaq, SRTSOCKET ns, int errorcode, co * `token`: The token value, if it was used for group connection, otherwise -1 -## Socket group management +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + + +## Socket Group Management * [SRT_GROUP_TYPE](#SRT_GROUP_TYPE) * [SRT_SOCKGROUPCONFIG](#SRT_SOCKGROUPCONFIG) * [SRT_SOCKGROUPDATA](#SRT_SOCKGROUPDATA) * [SRT_MEMBERSTATUS](#SRT_MEMBERSTATUS) - -[Functions to be used on groups](#functions-to-be-used-on-groups): - - * [srt_create_group](#srt_create_group) - * [srt_include](#srt_include) - * [srt_exclude](#srt_exclude) - * [srt_groupof](#srt_groupof) - * [srt_group_data](#srt_group_data) - * [srt_connect_group](#srt_connect_group) - * [srt_prepare_endpoint](#srt_prepare_endpoint) - * [srt_create_config](#srt_create_config) - * [srt_delete_config](#srt_delete_config) - * [srt_config_add](#srt_config_add) - - ### SRT_GROUP_TYPE The following group types are collected in an [`SRT_GROUP_TYPE`](#SRT_GROUP_TYPE) enum: @@ -1142,11 +1134,24 @@ as the only active one, this link will be "silenced" (its state will become `SRT_GST_IDLE`). +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- -## Functions to be used on groups: +### Functions to Be Used on Groups -### srt_create_group + * [srt_create_group](#srt_create_group) + * [srt_include](#srt_include) + * [srt_exclude](#srt_exclude) + * [srt_groupof](#srt_groupof) + * [srt_group_data](#srt_group_data) + * [srt_connect_group](#srt_connect_group) + * [srt_prepare_endpoint](#srt_prepare_endpoint) + * [srt_create_config](#srt_create_config) + * [srt_delete_config](#srt_delete_config) + * [srt_config_add](#srt_config_add) + +#### srt_create_group ``` SRTSOCKET srt_create_group(SRT_GROUP_TYPE type); @@ -1162,7 +1167,7 @@ the `SRTGROUP_MASK` bit is set on it, unlike for socket ID. --- -### srt_include +#### srt_include ``` int srt_include(SRTSOCKET socket, SRTSOCKET group); @@ -1172,12 +1177,11 @@ This function adds a socket to a group. This is only allowed for unmanaged groups. No such group type is currently implemented. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_exclude +#### srt_exclude ``` int srt_exclude(SRTSOCKET socket); @@ -1187,12 +1191,11 @@ This is only allowed for unmanaged groups. No such group type is currently implemented. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_groupof +#### srt_groupof ``` SRTSOCKET srt_groupof(SRTSOCKET socket); @@ -1202,12 +1205,11 @@ Returns the group ID of the socket, or `SRT_INVALID_SOCK` if the socket doesn't exist or it's not a member of any group. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_group_data +#### srt_group_data ``` int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); @@ -1239,14 +1241,13 @@ and providing `socketgroup` and `inoutlen`. | -1 | Error | | | | + | Errors | | |:---------------------------------- |:--------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | Reported if `socketgroup` is not an existing group ID | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Reported if `inoutlen` if less than the size of the group | | | | - - | in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | |:---------:|:--------------:|:------------:|:----------:|:------------:|:---------------------------------:| @@ -1257,12 +1258,11 @@ and providing `socketgroup` and `inoutlen`. | ptr | < group.size | -1 | āœ–ļø | group.size | [`SRT_ELARGEMSG`](#srt_elargemsg) | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_connect_group +#### srt_connect_group ``` int srt_connect_group(SRTSOCKET group, @@ -1276,7 +1276,7 @@ in `name` array. However if you did this in blocking mode, the first call to [`srt_connect`](#srt_connect) would block until the connection is established, whereas this function blocks until any of the specified connections is established. -If you set the group nonblocking mode ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) +If you set the group nonblocking mode ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) option), there's no difference, except that the [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure allows you to add extra configuration data used by groups. Note also that this function accepts only groups, not sockets. @@ -1367,13 +1367,11 @@ define a unique value for the `token`. Your application can also set unique valu in which case the `token` value will be preserved. - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_prepare_endpoint +#### srt_prepare_endpoint ``` SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, @@ -1421,7 +1419,7 @@ the [`errorcode`](#error-codes) field. --- -### srt_create_config +#### srt_create_config ``` SRT_SOCKOPT_CONFIG* srt_create_config(); @@ -1443,7 +1441,7 @@ should delete it using [`srt_delete_config`](#srt_delete_config). --- -### srt_delete_config +#### srt_delete_config ``` void srt_delete_config(SRT_SOCKOPT_CONFIG* c); @@ -1456,7 +1454,7 @@ Deletes the configuration object. --- -### srt_config_add +#### srt_config_add ``` int srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); @@ -1476,16 +1474,16 @@ on every socket, you should instead set this option on the whole group. The following options are allowed to be set on the member socket: -* [`SRTO_SNDBUF`](../docs/APISocketOptions.md#SRTO_SNDBUF): Allows for larger sender buffer for slower links -* [`SRTO_RCVBUF`](../docs/APISocketOptions.md#SRTO_RCVBUF): Allows for larger receiver buffer for longer recovery -* [`SRTO_UDP_RCVBUF`](../docs/APISocketOptions.md#SRTO_UDP_RCVBUF): UDP receiver buffer, if this link has a big flight window -* [`SRTO_UDP_SNDBUF`](../docs/APISocketOptions.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window -* [`SRTO_SNDDROPDELAY`](../docs/APISocketOptions.md#SRTO_SNDDROPDELAY): When particular link tends to drop too eagerly -* [`SRTO_NAKREPORT`](../docs/APISocketOptions.md#SRTO_NAKREPORT): If you don't want NAKREPORT to work for this link -* [`SRTO_CONNTIMEO`](../docs/APISocketOptions.md#SRTO_CONNTIMEO): If you want to give more time to connect on this link -* [`SRTO_LOSSMAXTTL`](../docs/APISocketOptions.md#SRTO_LOSSMAXTTL): If this link tends to suffer from UDP reordering -* [`SRTO_PEERIDLETIMEO`](../docs/APISocketOptions.md#SRTO_PEERIDLETIMEO): If you want to be more tolerant for temporary outages -* [`SRTO_GROUPSTABTIMEO`](../docs/APISocketOptions.md#SRTO_GROUPSTABTIMEO): To set ACK jitter tolerance per individual link +* [`SRTO_SNDBUF`](API-socket-options.md#SRTO_SNDBUF): Allows for larger sender buffer for slower links +* [`SRTO_RCVBUF`](API-socket-options.md#SRTO_RCVBUF): Allows for larger receiver buffer for longer recovery +* [`SRTO_UDP_RCVBUF`](API-socket-options.md#SRTO_UDP_RCVBUF): UDP receiver buffer, if this link has a big flight window +* [`SRTO_UDP_SNDBUF`](API-socket-options.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window +* [`SRTO_SNDDROPDELAY`](API-socket-options.md#SRTO_SNDDROPDELAY): When particular link tends to drop too eagerly +* [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT): If you don't want NAKREPORT to work for this link +* [`SRTO_CONNTIMEO`](API-socket-options.md#SRTO_CONNTIMEO): If you want to give more time to connect on this link +* [`SRTO_LOSSMAXTTL`](API-socket-options.md#SRTO_LOSSMAXTTL): If this link tends to suffer from UDP reordering +* [`SRTO_PEERIDLETIMEO`](API-socket-options.md#SRTO_PEERIDLETIMEO): If you want to be more tolerant for temporary outages +* [`SRTO_GROUPSTABTIMEO`](API-socket-options.md#SRTO_GROUPSTABTIMEO): To set ACK jitter tolerance per individual link | Returns | | @@ -1502,9 +1500,12 @@ The following options are allowed to be set on the member socket: [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- + -## Options and properties + +## Options and Properties * [srt_getpeername](#srt_getpeername) * [srt_getsockname](#srt_getsockname) @@ -1512,10 +1513,8 @@ The following options are allowed to be set on the member socket: * [srt_setsockopt, srt_setsockflag](#srt_setsockopt-srt_setsockflag) * [srt_getversion](#srt_getversion) +**NOTE**: For more information, see [Getting and Setting Options](API-socket-options.md#getting-and-setting-options). -**NOTE**: For more information, see [Getting and Setting Options](../docs/APISocketOptions.md#getting-and-setting-options) - - ### srt_getpeername ``` int srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); @@ -1636,7 +1635,6 @@ are then derived by the member sockets. specific option (see option description for details). - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1656,11 +1654,22 @@ readable form, where x = ("%d", (version>>16) & 0xff), etc. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + -## Helper data types for transmission - +## Helper Data Types for Transmission + +* [SRT_MSGCTRL](#SRT_MSGCTRL) + +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. + ### SRT_MSGCTRL The [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure: @@ -1668,15 +1677,15 @@ The [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure: ```c++ typedef struct SRT_MsgCtrl_ { - int flags; // Left for future - int msgttl; // TTL for a message, default -1 (no TTL limitation) - int inorder; // Whether a message is allowed to supersede a partially lost one. Unused in stream and live mode. - int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame - int64_t srctime; // source time (microseconds since SRT internal clock epoch) - int32_t pktseq; // sequence number of the first packet in received message (unused for sending) - int32_t msgno; // message number (output value for both sending and receiving) - SRT_SOCKGROUPDATA* grpdata; // pointer to group data array - size_t grpdata_size; // size of the group array + int flags; // Left for future + int msgttl; // TTL for a message, default -1 (no TTL limitation) + int inorder; // Whether a message is allowed to supersede a partially lost one. Unused in stream and live mode + int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame + int64_t srctime; // Source time, in microseconds since SRT internal clock epoch + int32_t pktseq; // Sequence number of the first packet in received message (unused for sending) + int32_t msgno; // Message number (output value for both sending and receiving) + SRT_SOCKGROUPDATA* grpdata; // Pointer to group data array + size_t grpdata_size; // Size of the group array } SRT_MSGCTRL; ``` @@ -1729,8 +1738,8 @@ call [`srt_sendmsg2`](#srt_sendmsg) or [`srt_recvmsg2`](#srt_recvmsg2) function for a group, you should pass an array here so that you can retrieve the status of particular member sockets. If you pass an array that is too small, your `grpdata_size` field will be rewritten with the current number of members, but without filling in -the array. For details, see the (Bonding introduction)[bonding-intro.md] and -(Socket Groups)[socket-groups.md] documents. +the array. For details, see the (Bonding introduction)[../bonding-intro.md] and +(Socket Groups)[../socket-groups.md] documents. **Helpers for [`SRT_MSGCTRL`](#SRT_MSGCTRL):** @@ -1746,6 +1755,11 @@ pass this constant object into any of the API functions because they require it to be mutable, as they use some fields to output values. +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + ## Transmission @@ -1754,7 +1768,12 @@ to be mutable, as they use some fields to output values. * [srt_recv, srt_recvmsg, srt_recvmsg2](#srt_recv-srt_recvmsg-srt_recvmsg2) * [srt_sendfile, srt_recvfile](#srt_sendfile-srt_recvfile) - +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. + + ### srt_send ### srt_sendmsg ### srt_sendmsg2 @@ -1767,7 +1786,6 @@ int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); Sends a payload to a remote party over a given socket. - **Arguments**: * [`u`](#u): Socket used to send. The socket must be connected for this operation. @@ -1812,14 +1830,12 @@ In both **file/message** and **live mode** the successful return is always equal | [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
**live mode**: trying to send more bytes at once than `SRTO_PAYLOADSIZE` or wrong source time
was provided. | | [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | Incorrect API usage in **stream mode** (reserved for future use):
The congestion controller object used for this mode doesn't use any restrictions on this call,
but this may change. | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Message to be sent can't fit in the sending buffer (that is, it exceeds the current total space in the
sending buffer in bytes). This means that the sender buffer is too small, or the application is
trying to send a larger message than initially predicted. | -| [`SRT_EASYNCSND`](#srt_easyncsnd) | There's no free space currently in the buffer to schedule the payload. This is only reported in
non-blocking mode ([`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) set to false); in blocking mode the call is blocked until
enough free space in the sending buffer becomes available. | -| [`SRT_ETIMEOUT`](#srt_etimeout) | The condition described above still persists and the timeout has passed. This is only reported in
blocking mode when [`SRTO_SNDTIMEO`](../docs/APISocketOptions.md#SRTO_SNDTIMEO) is set to a value other than -1. | +| [`SRT_EASYNCSND`](#srt_easyncsnd) | There's no free space currently in the buffer to schedule the payload. This is only reported in
non-blocking mode ([`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) set to false); in blocking mode the call is blocked until
enough free space in the sending buffer becomes available. | +| [`SRT_ETIMEOUT`](#srt_etimeout) | The condition described above still persists and the timeout has passed. This is only reported in
blocking mode when [`SRTO_SNDTIMEO`](API-socket-options.md#SRTO_SNDTIMEO) is set to a value other than -1. | | [`SRT_EPEERERR`](#srt_epeererr) | This is reported only in the case where, as a stream is being received by a peer, the
[`srt_recvfile`](#srt_recvfile) function encounters an error during a write operation on a file. This is reported by
a `UMSG_PEERERROR` message from the peer, and the agent sets the appropriate flag internally.
This flag persists up to the moment when the connection is broken or closed. | | | | - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1840,7 +1856,7 @@ Extracts the payload waiting to be received. Note that [`srt_recv`](#srt_recv) a kept for historical reasons. In the UDT predecessor the application was required to use either the `UDT::recv` version for **stream mode** and `UDT::recvmsg` for **message mode**. In SRT this distinction is resolved internally by the -[`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) flag. +[`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag. **Arguments**: @@ -1866,8 +1882,8 @@ the error is reported. 3. In **live mode**, the function behaves as in **file/message mode**, although the number of bytes retrieved will be at most the size of `SRTO_PAYLOADSIZE`. In this mode, -however, with default settings of [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) -and [`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP), the message will be +however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) +and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP), the message will be received only when its time to play has come, and until then it will be kept in the receiver buffer. Also, when the time to play has come for a message that is next to the currently lost one, it will be delivered and the lost one dropped. @@ -1883,15 +1899,14 @@ the currently lost one, it will be delivered and the lost one dropped. |:--------------------------------------------- |:--------------------------------------------------------- | | [`SRT_ENOCONN`](#srt_enoconn) | Socket [`u`](#u) used for the operation is not connected. | | [`SRT_ECONNLOST`](#srt_econnlost) | Socket [`u`](#u) used for the operation has lost connection (this is reported only if the connection
was unexpectedly broken, not when it was closed by the foreign host). | -| [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
-- **live mode**: size of the buffer is less than [`SRTO_PAYLOADSIZE`](../docs/APISocketOptions.md#SRTO_PAYLOADSIZE) | +| [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
-- **live mode**: size of the buffer is less than [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) | | [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | Incorrect API usage in **stream mode**:
ā€¢ Currently not in use. File congestion control used for **stream mode** does not restrict
the parameters. :warning:   **???** | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Message to be sent can't fit in the sending buffer (that is, it exceeds the current total space in
the sending buffer in bytes). This means that the sender buffer is too small, or the application
is trying to send a larger message than initially intended. | -| [`SRT_EASYNCRCV`](#srt_easyncrcv) | There are no data currently waiting for delivery. This happens only in non-blocking mode
(when [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to false). In blocking mode the call is blocked until the data are ready.
How this is defined, depends on the mode:
ā€¢ In **live mode** (with [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) on), at least one packet must be present in the receiver
buffer and its time to play be in the past
ā€¢ In **file/message mode**, one full message must be available, the next one waiting if there are no
messages with `inorder` = false, or possibly the first message ready with `inorder` = false
ā€¢ In **file/stream mode**, it is expected to have at least one byte of data still not extracted | -| [`SRT_ETIMEOUT`](#srt_etimeout) | The readiness condition described above is still not achieved and the timeout has passed.
This is only reported in blocking mode when[`SRTO_RCVTIMEO`](../docs/APISocketOptions.md#SRTO_RCVTIMEO) is set to a value other than -1. | +| [`SRT_EASYNCRCV`](#srt_easyncrcv) | There are no data currently waiting for delivery. This happens only in non-blocking mode
(when [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to false). In blocking mode the call is blocked until the data are ready.
How this is defined, depends on the mode:
ā€¢ In **live mode** (with [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) on), at least one packet must be present in the receiver
buffer and its time to play be in the past
ā€¢ In **file/message mode**, one full message must be available, the next one waiting if there are no
messages with `inorder` = false, or possibly the first message ready with `inorder` = false
ā€¢ In **file/stream mode**, it is expected to have at least one byte of data still not extracted | +| [`SRT_ETIMEOUT`](#srt_etimeout) | The readiness condition described above is still not achieved and the timeout has passed.
This is only reported in blocking mode when[`SRTO_RCVTIMEO`](API-socket-options.md#SRTO_RCVTIMEO) is set to a value other than -1. | | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1939,7 +1954,7 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or |:--------------------------------------------- |:----------------------------------------------------------------------------- | | [`SRT_ENOCONN`](#srt_enoconn) | Socket [`u`](#u) used for the operation is not connected. | | [`SRT_ECONNLOST`](#srt_econnlost) | Socket [`u`](#u) used for the operation has lost its connection. | -| [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | When socket has [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) = true or [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) = true.
(:warning:   **BUG?**: Looxlike MESSAGEAPI isn't checked) | +| [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | When socket has [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = true or [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = true.
(:warning:   **BUG?**: Looxlike MESSAGEAPI isn't checked) | | [`SRT_EINVRDOFF`](#srt_einvrdoff) | There is a mistake in `offset` or `size` parameters, which should match the index availability
and size of the bytes available since `offset` index. This is actually reported for [`srt_sendfile`](#srt_sendfile)
when the `seekg` or `tellg` operations resulted in error. | | [`SRT_EINVWROFF`](#srt_einvwroff) | Like above, reported for [`srt_recvfile`](#srt_recvfile) and `seekp`/`tellp`. | | [`SRT_ERDPERM`](#srt_erdperm) | The read from file operation has failed ([`srt_sendfile`](#srt_sendfile)). | @@ -1947,14 +1962,18 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- + + + +## Performance Tracking -## Performance tracking +* [srt_bstats, srt_bistats](#srt_bstats-srt_bistats) -**Sequence Numbers** +**Sequence Numbers:** The sequence numbers used in SRT are 32-bit "circular numbers" with the most significant bit not included. For example 0x7FFFFFFF shifted forward by 3 becomes 2. As far as any comparison is concerned, it can be thought of as a "distance" which is an integer @@ -1967,9 +1986,6 @@ the required range already, so for a numbers like 0x7FFFFFF0 and 0x10, for which "numeric difference" would be 0x7FFFFFE0, the "distance" is 0x20. -* [srt_bstats, srt_bistats](#srt_bstats-srt_bistats) - - ### srt_bstats ### srt_bistats ``` @@ -1990,11 +2006,17 @@ Reports the current statistics * `instantaneous`: 1 if the statistics should use instant data, not moving averages `SRT_TRACEBSTATS` is an alias to `struct CBytePerfMon`. For a complete description -of the fields please refer to the document [statistics.md](statistics.md). +of the fields please refer to [SRT Statistics](statistics.md). +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) -## Asynchronous operations (epoll) +--- + + + + +## Asynchronous Operations (Epoll) * [srt_epoll_create](#srt_epoll_create) * [srt_epoll_add_usock, srt_epoll_add_ssock, srt_epoll_update_usock, srt_epoll_update_ssock](#srt_epoll_add_usock-srt_epoll_add_ssock-srt_epoll_update_usock-srt_epoll_update_ssock) @@ -2012,8 +2034,8 @@ or writing operation, as it's in blocking mode, it blocks until at least one of the sockets subscribed for a single waiting call in given operation mode is ready to do this operation without blocking. It's usually combined with setting the nonblocking mode on a socket. In SRT this is set separately for reading and -writing ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) and -[`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) respectively). This is +writing ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) and +[`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) respectively). This is to ensure that if there is internal error in the application (or even possibly a bug in SRT that has reported a spurious readiness report) the operation will end up with an error rather than cause blocking, which would be more dangerous for the @@ -2026,9 +2048,6 @@ readiness status of particular operations. The [`srt_epoll_wait`](#srt_epoll_wai function can then be used to block until any readiness status in the whole [`eid`](#eid) is set. - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_epoll_create ``` @@ -2107,7 +2126,7 @@ With [`SRT_EPOLL_ET`](#SRT_EPOLL_ET) flag they become **edge-triggered**. The [`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag is always edge-triggered. It designates a special event that happens on a group, or on a listener socket that -has the [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) flag +has the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag set to allow group connections. This flag is triggered in the following situations: * for group connections, when a new link has been established for a group that @@ -2251,7 +2270,6 @@ the only way to know what kind of error has occurred on the socket. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2338,7 +2356,6 @@ container identified by [`eid`](#eid). | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2373,8 +2390,8 @@ the general output array is not empty. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------- | -| | This function returns the state of the flags at the time before the call. | -| -1 | Special value in case when an error occurred. | +| | This function returns the state of the flags at the time before the call | +| -1 | Special value in case when an error occurred | | | | | Errors | | @@ -2396,8 +2413,8 @@ Deletes the epoll container. | Returns | | |:----------------------------- |:-------------------------------------------------------------- | -| | The number (\>0) of ready sockets, of whatever kind (if any). | -| -1 | Error . | +| | The number (\>0) of ready sockets, of whatever kind (if any) | +| -1 | Error | | | | | Errors | | @@ -2406,9 +2423,14 @@ Deletes the epoll container. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + -## Logging control +## Logging Control * [srt_setloglevel](#srt_setloglevel) * [srt_addlogfa, srt_dellogfa, srt_resetlogfa](#srt_addlogfa-srt_dellogfa-srt_resetlogfa) @@ -2424,10 +2446,6 @@ entries up to the *Note* log level are displayed and from all FAs. Logging can only be manipulated globally, with no regard to a specific socket. This is because lots of operations in SRT are not dedicated to any particular socket, and some are shared between sockets. - - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_setloglevel @@ -2513,6 +2531,7 @@ The following flags are available, as collected in the `logging_api.h` public he - `SRT_LOGF_DISABLE_SEVERITY`: Do not provide severity information in the header - `SRT_LOGF_DISABLE_EOL`: Do not add the end-of-line character to the log line + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2579,10 +2598,6 @@ The clock used by the SRT internal clock is determined by the following build fl The default is currently to use the system clock as the internal SRT clock, although it's highly recommended to use one of the above monotonic clocks, as system clock is vulnerable to time modifications during transmission. - - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_time_now @@ -2612,11 +2627,11 @@ Get connection time in microseconds elapsed since epoch using SRT internal clock (steady or monotonic clock). The connection time represents the time when SRT socket was open to establish a connection. Milliseconds elapsed since connection start time can be determined using [**Performance tracking**](#Performance-tracking) functions -and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [statistics.md](statistics.md)). +and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistics.md)). | Returns | | |:----------------------------- |:--------------------------------------------------------------------------- | -| | Connection time in microseconds elapsed since epoch of SRT internal clock. | +| | Connection time in microseconds elapsed since epoch of SRT internal clock | | -1 | Error | | | | @@ -2630,17 +2645,10 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [statistics.md](statistics --- -## Diagnostics -General notes concerning the `getlasterror` diagnostic functions: when an API -function ends up with error, this error information is stored in a thread-local -storage. This means that you'll get the error of the operation that was last -performed as long as you call this diagnostic function just after the failed -function has returned. In any other situation the information provided by the -diagnostic function is undefined. -**NOTE**: There is a list of [Error Codes](#error-codes) at the bottom of this document. +## Diagnostics * [srt_getlasterror_str](#srt_getlasterror_str) * [srt_getlasterror](#srt_getlasterror) @@ -2650,7 +2658,16 @@ diagnostic function is undefined. * [srt_rejectreason_str](#srt_rejectreason_str) * [srt_setrejectreason](#srt_setrejectreason) - +General notes concerning the `getlasterror` diagnostic functions: when an API +function ends up with error, this error information is stored in a thread-local +storage. This means that you'll get the error of the operation that was last +performed as long as you call this diagnostic function just after the failed +function has returned. In any other situation the information provided by the +diagnostic function is undefined. + +**NOTE**: There are lists of rejection reasons and error codes at the bottom of this section. + + ### srt_getlasterror ``` @@ -2683,7 +2700,6 @@ as long as only one thread in the whole application calls this function at the moment* - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2714,7 +2730,6 @@ report a "successful" code. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - --- ### srt_rejectreason_str @@ -2751,7 +2766,7 @@ back to the caller peer with the handshake response. Note that allowed values for this function begin with `SRT_REJC_PREDEFINED` (that is, you cannot set a system rejection code). For example, your application can inform the calling side that the resource specified under the `r` key in the -StreamID string (see [`SRTO_STREAMID`](../docs/APISocketOptions.md#SRTO_STREAMID)) +StreamID string (see [`SRTO_STREAMID`](API-socket-options.md#SRTO_STREAMID)) is not available - it then sets the value to `SRT_REJC_PREDEFINED + 404`. | Returns | | @@ -2766,8 +2781,8 @@ is not available - it then sets the value to `SRT_REJC_PREDEFINED + 404`. | [`SRT_EINVPARAM`](#srt_einvparam) | `value` is less than `SRT_REJC_PREDEFINED` | | | | -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2779,16 +2794,20 @@ int srt_getrejectreason(SRTSOCKET sock); This function provides a more detailed reason for a failed connection attempt. It shall be called after a connecting function (such as [`srt_connect`](#srt_connect)) has returned an error, the code for which is [`SRT_ECONNREJ`](#srt_econnrej). If -[`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) has been set on the socket +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) has been set on the socket used for the connection, the function should also be called when the [`SRT_EPOLL_ERR`](#SRT_EPOLL_ERR) event is set for this socket. It returns a numeric code, which can be translated into a message by [`srt_rejectreason_str`](#srt_rejectreason_str). - -## Rejection Reasons - +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### Rejection Reasons + + #### SRT_REJ_UNKNOWN A fallback value for cases when there was no connection rejected. @@ -2817,8 +2836,6 @@ The data sent by one party to another cannot be properly interpreted. This should not happen during normal usage, unless it's a bug, or some weird events are happening on the network. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_BACKLOG @@ -2857,8 +2874,6 @@ the sent handshake packets are returning to the same host as if they were sent by the peer (i.e. a party is sending to itself). When this happens, this reject reason will be reported by every attempt. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_BADSECRET @@ -2868,28 +2883,26 @@ Both parties have defined a passphrase for connection, but they differ. #### SRT_REJ_UNSECURE Only one connection party has set up a password. See also the -[`SRTO_ENFORCEDENCRYPTION`](../docs/APISocketOptions.md#SRTO_ENFORCEDENCRYPTION) flag. +[`SRTO_ENFORCEDENCRYPTION`](API-socket-options.md#SRTO_ENFORCEDENCRYPTION) flag. #### SRT_REJ_MESSAGEAPI -The value of the [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) +The value of the [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties. #### SRT_REJ_CONGESTION -The [`SRTO_CONGESTION`](../docs/APISocketOptions.md#SRTO_CONGESTION)option has +The [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION)option has been set up differently on both connection parties. #### SRT_REJ_FILTER -The [`SRTO_PACKETFILTER`](../docs/APISocketOptions.md#SRTO_PACKETFILTER) option +The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_GROUP @@ -2905,7 +2918,7 @@ completely different from the existing connections in the bonding group. The connection wasn't rejected, but it timed out. This code is always set on connection timeout, but this is the only way to get this state in non-blocking -mode (see [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN)). +mode (see [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN)). There may also be server and user rejection codes, as defined by the `SRT_REJC_INTERNAL`, `SRT_REJC_PREDEFINED` and `SRT_REJC_USERDEFINED` @@ -2917,9 +2930,9 @@ the application. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - - -## Error Codes +--- + +### Error Codes All functions that return the status via `int` value return -1 (designated as `SRT_ERROR`) always when the call has failed (in case of resource creation @@ -2929,50 +2942,48 @@ functions an appropriate symbol is defined, like `SRT_INVALID_SOCK` for `SRT_ERRNO` enum: -#### `SRT_EUNKNOWN` +#### SRT_EUNKNOWN Internal error when setting the right error code. -#### `SRT_SUCCESS` +#### SRT_SUCCESS The value set when the last error was cleared and no error has occurred since then. -#### `SRT_ECONNSETUP` +#### SRT_ECONNSETUP General setup error resulting from internal system state. -#### `SRT_ENOSERVER` +#### SRT_ENOSERVER Connection timed out while attempting to connect to the remote address. Note that when this happens, [`srt_getrejectreason`](#srt_getrejectreason) also reports the timeout reason. -#### `SRT_ECONNREJ` +#### SRT_ECONNREJ Connection has been rejected. Additional reject reason can be obtained through [`srt_getrejectreason`](#srt_getrejectreason) (see above). -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_ESOCKFAIL` +#### SRT_ESOCKFAIL An error occurred when trying to call a system function on an internally used UDP socket. Note that the detailed system error is available in the extra variable passed by pointer to `srt_getlasterror`. -#### `SRT_ESECFAIL` +#### SRT_ESECFAIL A possible tampering with the handshake packets was detected, or an encryption request wasn't properly fulfilled. -#### `SRT_ESCLOSED` +#### SRT_ESCLOSED A socket that was vital for an operation called in blocking mode has been closed during the operation. Please note that this situation is @@ -2984,85 +2995,79 @@ parameter to [`srt_connect*`](#srt_connect) or [`srt_accept`](#srt_accept) is no longer usable. -#### `SRT_ECONNFAIL` +#### SRT_ECONNFAIL General connection failure of unknown details. -#### `SRT_ECONNLOST` +#### SRT_ECONNLOST The socket was properly connected, but the connection has been broken. This specialization is reported from the transmission functions. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_ENOCONN` +#### SRT_ENOCONN The socket is not connected. This can be reported also when the connection was broken for a function that checks some characteristic socket data. -#### `SRT_ERESOURCE` +#### SRT_ERESOURCE System or standard library error reported unexpectedly for unknown purpose. Usually it means some internal error. -#### `SRT_ETHREAD` +#### SRT_ETHREAD System was unable to spawn a new thread when required. -#### `SRT_ENOBUF` +#### SRT_ENOBUF System was unable to allocate memory for buffers. -#### `SRT_ESYSOBJ` +#### SRT_ESYSOBJ System was unable to allocate system specific objects (such as sockets, mutexes or condition variables). -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EFILE` +#### SRT_EFILE General filesystem error (for functions operating with file transmission). -#### `SRT_EINVRDOFF` +#### SRT_EINVRDOFF Failure when trying to read from a given position in the file (file could be modified while it was read from). -#### `SRT_ERDPERM` +#### SRT_ERDPERM Read permission was denied when trying to read from file. -#### `SRT_EINVWROFF` +#### SRT_EINVWROFF Failed to set position in the written file. -#### `SRT_EWRPERM` +#### SRT_EWRPERM Write permission was denied when trying to write to a file. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EINVOP` +#### SRT_EINVOP Invalid operation performed for the current state of a socket. This mainly -concerns performing `srt_bind*` operations on a socket that is already bound. +concerns performing `srt_bind*` operations on a socket that is already bound. Once a socket has been been bound, it cannot be bound again. -#### `SRT_EBOUNDSOCK` +#### SRT_EBOUNDSOCK The socket is currently bound and the required operation cannot be performed in this state. Usually it's about an option that can only @@ -3070,7 +3075,7 @@ be set on the socket before binding (`srt_bind*`). Note that a socket that is currently connected is also considered bound. -#### `SRT_ECONNSOCK` +#### SRT_ECONNSOCK The socket is currently connected and therefore performing the required operation is not possible. Usually concerns setting an option that must be set before @@ -3080,7 +3085,7 @@ isn't in a state that allows it (only [`SRTS_INIT`](#SRTS_INIT) or [`SRTS_OPENED`](#SRTS_OPENED) are allowed). -#### `SRT_EINVPARAM` +#### SRT_EINVPARAM This error is reported in a variety of situations when call parameters for API functions have some requirements defined and these were not @@ -3089,17 +3094,15 @@ parameters of the call before even performing any operation. This error can be easily avoided if you set the values correctly. -#### `SRT_EINVSOCK` +#### SRT_EINVSOCK The API function required an ID of an entity (socket or group) and it was invalid. Note that some API functions work only with socket or only with group, so they would also return this error if inappropriate type of entity was passed, even if it was valid. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EUNBOUNDSOCK` +#### SRT_EUNBOUNDSOCK The operation to be performed on a socket requires that it first be explicitly bound (using [`srt_bind*`](#srt_bind) functions). Currently it applies when @@ -3107,23 +3110,23 @@ calling [`srt_listen`](#srt_listen), which cannot work with an implicitly bound socket. -#### `SRT_ENOLISTEN` +#### SRT_ENOLISTEN The socket passed for the operation is required to be in the listen state ([`srt_listen`](#srt_listen) must be called first). -#### `SRT_ERDVNOSERV` +#### SRT_ERDVNOSERV The required operation cannot be performed when the socket is set to rendezvous -mode ([`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) set to true). +mode ([`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) set to true). Usually applies when trying to call [`srt_listen`](#srt_listen) on such a socket. -#### `SRT_ERDVUNBOUND` +#### SRT_ERDVUNBOUND An attempt was made to connect to a socket set to rendezvous mode -([`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) set to true) +([`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) set to true) that was not first bound. A rendezvous connection requires setting up two addresses and ports on both sides of the connection, then setting the local one with [`srt_bind`](#srt_bind) and using the remote one with [`srt_connect`](#srt_connect) @@ -3133,42 +3136,40 @@ state) that is to be bound implicitly is only allowed for regular caller sockets (not rendezvous). -#### `SRT_EINVALMSGAPI` +#### SRT_EINVALMSGAPI The function was used incorrectly in the message API. This can happen if: * The parameters specific for the message API in [`SRT_MSGCTRL`](#SRT_MSGCTRL) -type parameter were incorrectly specified +type parameter were incorrectly specified. -* The extra parameter check performed by the congestion controller has failed +* The extra parameter check performed by the congestion controller has failed. * The socket is a member of a self-managing group, therefore you should -perform the operation on the group, not on this socket - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +perform the operation on the group, not on this socket. -#### `SRT_EINVALBUFFERAPI` +#### SRT_EINVALBUFFERAPI The function was used incorrectly in the stream (buffer) API, that is, either the stream-only functions were used with set message API ([`srt_sendfile`](#srt_sendfile)/[`srt_recvfile`](#srt_recvfile)) -or TSBPD mode was used with buffer API ([`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) set to true) +or TSBPD mode was used with buffer API ([`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) set to true) or the congestion controller has failed to check call parameters. -#### `SRT_EDUPLISTEN` +#### SRT_EDUPLISTEN The port tried to be bound for listening is already busy. Note that binding to the same port -is allowed in general (when [`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) +is allowed in general (when [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is true on every socket that has bound it), but only one such socket can be a listener. -#### `SRT_ELARGEMSG` +#### SRT_ELARGEMSG Size exceeded. This is reported in the following situations: * Trying to receive a message, but the read-ready message is larger than -the buffer passed to the receiving function +the buffer passed to the receiving function. * Trying to send a message, but the size of this message exceeds the size of the preset sender buffer, so it cannot be stored in the sender buffer. @@ -3176,12 +3177,12 @@ size of the preset sender buffer, so it cannot be stored in the sender buffer. * When getting group data, the array to be filled is too small. -#### `SRT_EINVPOLLID` +#### SRT_EINVPOLLID -The epoll ID passed to an epoll function is invalid +The epoll ID passed to an epoll function is invalid. -#### `SRT_EPOLLEMPTY` +#### SRT_EPOLLEMPTY The epoll container currently has no subscribed sockets. This is reported by an epoll waiting function that would in this case block forever. This problem @@ -3193,50 +3194,48 @@ by setting the `SRT_EPOLL_ENABLE_EMPTY` flag, which may be useful when you use multiple threads and start waiting without subscribed sockets, so that you can subscribe them later from another thread. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EASYNCFAIL` +#### SRT_EASYNCFAIL General asynchronous failure (not in use currently). -#### `SRT_EASYNCSND` +#### SRT_EASYNCSND Sending operation is not ready to perform. This error is reported when trying to perform a sending operation on a socket that is not ready for sending, but -[`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) was set to false (when +[`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) was set to false (when true, the function would block the call otherwise). -#### `SRT_EASYNCRCV` +#### SRT_EASYNCRCV Receiving operation is not ready to perform. This error is reported when trying to perform a receiving operation or accept a new socket from the listener socket, when -the socket is not ready for that operation, but [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) +the socket is not ready for that operation, but [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) was set to false (when true, the function would block the call otherwise). -#### `SRT_ETIMEOUT` +#### SRT_ETIMEOUT The operation timed out. This can happen if you have a timeout set by an option -([`SRTO_RCVTIMEO`](../docs/APISocketOptions.md#SRTO_RCVTIMEO) or -[`SRTO_SNDTIMEO`](../docs/APISocketOptions.md#SRTO_SNDTIMEO)), or passed as an +([`SRTO_RCVTIMEO`](API-socket-options.md#SRTO_RCVTIMEO) or +[`SRTO_SNDTIMEO`](API-socket-options.md#SRTO_SNDTIMEO)), or passed as an extra argument ([`srt_epoll_wait`](#srt_epoll_wait) or [`srt_accept_bond`](#srt_accept_bond)) and the function call was blocking, but the required timeout time has passed. -#### `SRT_ECONGEST` +#### SRT_ECONGEST **NOTE**: This error is used only in an experimental version that requires setting the `SRT_ENABLE_ECN` macro at compile time. Otherwise the situation described below results in the usual successful report. This error should be reported by the sending function when, with -[`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) and -[`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) set to true, some +[`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and +[`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) set to true, some packets were dropped at the sender side (see the description of -[`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) for details). This +[`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) for details). This doesn't concern the data that were passed for sending by the sending function (these data are placed at the back of the sender buffer, while the dropped packets are at the front). In other words, the operation done by the sending @@ -3244,7 +3243,7 @@ function is successful, but the application might want to slow down the sending rate to avoid congestion. -#### `SRT_EPEERERR` +#### SRT_EPEERERR This error is reported when a receiver peer is writing to a file that an agent is sending. When the peer encounters an error when writing the received data to @@ -3252,7 +3251,4 @@ a file, it sends the `UMSG_PEERERROR` message back to the sender, and the sender reports this error from the API sending function. - - - -[RETURN TO TOP OF PAGE](#SRT-API-Functions) +[Return to Top of Page](#SRT-API-Functions) diff --git a/docs/APISocketOptions.md b/docs/API/API-socket-options.md similarity index 98% rename from docs/APISocketOptions.md rename to docs/API/API-socket-options.md index 149241a73..2dfc3bd8f 100644 --- a/docs/APISocketOptions.md +++ b/docs/API/API-socket-options.md @@ -1,18 +1,13 @@ -# SRT Socket Options +# SRT API Socket Options There is a general method of setting options on a socket in the SRT C API, similar to the system `setsockopt/getsockopt` functions. -**NOTE**: This document replaces the socket option description originally -in [api.md](https://github.com/Haivision/srt/blob/master/docs/API.md) +- [Types Used in Socket Options](#types-used-in-socket-options) +- [Getting and Setting Options](#getting-and-setting-options) +- [List of Options](#list-of-options) -**Sections:** - -- [Types used in socket options](#types-used-in-socket-options) -- [Getting and setting options](#getting-and-setting-options) -- [List of options](#list-of-options) - -## Types used in socket options +## Types Used in Socket Options Possible types of socket options are: @@ -20,7 +15,7 @@ Possible types of socket options are: does not change size on 64-bit systems. For clarity, options use this fixed size integer. In some cases the value is expressed using an enumeration type (see below). -- `int64_t` - Some options need the parameter specified as 64-bit integer +- `int64_t` - Some options need the parameter specified as 64-bit integer. - `bool` - Requires the use of a boolean type (`` for C, or built-in for C++). When *setting* an option, passing the value through an `int` type is @@ -36,7 +31,7 @@ read should specify the maximum length of that array. - `linger` - Linger structure. Used exclusively with `SRTO_LINGER`. -### Enumeration types used in options +### Enumeration Types Used in Options #### `SRT_TRANSTYPE` @@ -45,7 +40,7 @@ Used by `SRTO_TRANSTYPE` option: - `SRTT_LIVE`: Live mode. - `SRTT_FILE`: File mode. -See [Transmission types](./API.md#transmission-types) for details. +See [Transmission Types](API.md#transmission-types) for details. #### `SRT_KM_STATE` @@ -85,7 +80,7 @@ one party has set a password, in which case the KM state is as follows: | Party with no password: | `SRT_KM_S_NOSECRET` | `SRT_KM_S_UNSECURED` | | Party with password: | `SRT_KM_S_UNSECURED` | `SRT_KM_S_NOSECRET` | -## Getting and setting options +## Getting and Setting Options Legacy version: @@ -194,9 +189,9 @@ The + marker can only coexist with GS. Possible specifications are: `srt_create_config`. Note that this setting may override the setting derived from the group. -## List of options +## List of Options -The following table lists SRT socket options in alphabetical order. Option details are given further below. +The following table lists SRT API socket options in alphabetical order. Option details are given further below. | Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | | :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| @@ -775,7 +770,7 @@ complete (not all packets received or there was a packet loss) it will not be copied to the application's buffer. Messages that are sent later, but were earlier reassembled by the receiver, will be delivered once ready, if the `inorder` flag was set to false. -See [`srt_sendmsg`](https://github.com/Haivision/srt/blob/master/docs/API.md#sending-and-receiving)). +See [`srt_sendmsg`](API.md#sending-and-receiving)). As a comparison to the standard system protocols, the Stream API does transmission similar to TCP, whereas the Message API functions like the @@ -1432,7 +1427,7 @@ will be able to retrieve this stream ID from the socket that is returned from `srt_accept` (for a connected socket with that stream ID). You usually use SET on the socket used for `srt_connect`, and GET on the socket retrieved from `srt_accept`. This string can be used completely free-form. However, it's highly -recommended to follow the [SRT Access Control guidlines](AccessControl.md). +recommended to follow the [SRT Access Control guidlines](../AccessControl.md). - As this uses internally the `std::string` type, there are additional functions for it in the legacy/C++ API (udt.h): `srt::setstreamid` and `srt::getstreamid`. diff --git a/docs/API.md b/docs/API/API.md similarity index 93% rename from docs/API.md rename to docs/API/API.md index a124dac03..f1d3a1209 100644 --- a/docs/API.md +++ b/docs/API/API.md @@ -12,40 +12,25 @@ in `transmitmedia.*` files in the `apps` directory which is used by all applications. See `SrtSource::Read` and `SrtTarget::Write` as examples of how data are read and written in SRT. -- [Setup and teardown](#setup-and-teardown) -- [Creating and destroying a socket](#creating-and-destroying-a-socket) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Important Remarks](#important-remarks) -- [Binding and connecting](#binding-and-connecting) - - [Synopsis](#synopsis) - - [SRT Usage - listener (server)](#srt-usage---listener-server) - - [SRT Usage - rendezvous](#srt-usage---rendezvous) +- [Setup and Teardown](#setup-and-teardown) +- [Creating and Destroying a Socket](#creating-and-destroying-a-socket) +- [Binding and Connecting](#binding-and-connecting) - [Sending and Receiving](#sending-and-receiving) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Transmission types available in SRT](#transmission-types-available-in-srt) -- [Blocking and Non-blocking Mode](#blocking-and-non-blocking-mode) -- [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events)) - - [Synopsis](#synopsis) - - [SRT Usage](#srt-usage) - - [Transmission types](#transmission-types) - - [Terminology](#terminology) - - [Transmission method: Live](#transmission-method-live) - - [Transmission method: Buffer](#transmission-method-buffer) - - [Transmission method: Message](#transmission-method-message) - -**NOTE**: The socket option descriptions originally contained in this document -have been moved to [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md). - -## Setup and teardown +- [Blocking and Non-blocking Modes](#blocking-and-non-blocking-mode) + - [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events) +- [Transmission Types](#transmission-types) + - [Transmission Method: Live](#transmission-method-live) + - [Transmission Method: Buffer](#transmission-method-buffer) + - [Transmission Method: Message](#transmission-method-message) + +## Setup and Teardown Before any part of the SRT C API can be used, the user should call the `srt_startup()` function. Likewise, before the application exits, the `srt_cleanup()` function should be called. Note that one of the things the startup function does is to create a new thread, so choose the point of execution for these functions carefully. -## Creating and destroying a socket +## Creating and Destroying a Socket To do anything with SRT, you first have to create an SRT socket. The term "socket" in this case is used because of its logical similarity to system-wide sockets. @@ -92,7 +77,7 @@ port". However SRT offers more flexibility than UDP (or TCP, the more logical similarity) because it manages ports as its own resources. For example, one port may be shared between various services. -## Binding and connecting +## Binding and Connecting Connections are established using the same philosophy as TCP, using functions with names and signatures similar to the BSD Socket API. What is new here is @@ -253,7 +238,7 @@ forwarding SRT streams. It permits pulling and pushing of the sender's original time stamp, converted to local time and drift adjusted. The `srctime` parameter is the number of usec (since epoch) in local SRT clock time. If the connection is not between SRT peers or if **Timestamp-Based Packet Delivery mode (TSBPDMODE)** -is not enabled (see [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md)), +is not enabled (see [SRT API Socket Options](API-socket-options.md)), the extracted `srctime` will be 0. Passing `srctime = 0` in `sendmsg` is like using the API without `srctime` and the local send time will be used (if TSBPDMODE is enabled and receiver supports it). @@ -293,10 +278,10 @@ SRT_MSGCTRL mc = srt_msgctrl_default; nb = srt_recvmsg2(u, buf, nb, &mc); ``` -### Transmission types available in SRT +### Transmission Types Available in SRT Mode settings determine how the sender and receiver functions work. The main -[socket options](APISocketOptions.md) that control it are: +[socket options](API-socket-options.md) that control it are: - `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected mode: @@ -308,9 +293,9 @@ mode: See [Transmission types](#transmission-types) below. -## Blocking and Non-blocking Mode +## Blocking and Non-blocking Modes -SRT functions can also work in blocking and non-blocking mode, for which +SRT functions can also work in blocking and non-blocking modes, for which there are two separate options for sending and receiving: `SRTO_SNDSYN` and `SRTO_RCVSYN`. When blocking mode is used, a function will not exit until the availability condition is satisfied. In non-blocking mode the function @@ -323,13 +308,13 @@ and receiving. For example, `SNDSYN` defines blocking for `srt_connect` and `RCVSYN` defines blocking for `srt_accept`. The `SNDSYN` also makes `srt_close` exit only after the sending buffer is completely empty. -## EPoll (Non-blocking Mode Events) +### EPoll (Non-blocking Mode Events) EPoll is a mechanism to track the events happening on the sockets, both "system sockets" (see `SYSSOCKET` type) and SRT Sockets. Note that `SYSSOCKET` is also an alias for `int`, used only for clarity. -### Synopsis +#### Synopsis ```c++ int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); @@ -341,7 +326,7 @@ int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTim int srt_epoll_clear_usocks(int eid); ``` -### SRT Usage +#### Usage SRT socket being a user level concept, the system epoll (or other select) cannot be used to handle SRT non-blocking mode events. Instead, SRT provides a @@ -413,7 +398,7 @@ when system sockets are involved, is also 10ms. The return time from a poll function can only be quicker when there is an event raised on one of the active SRT sockets. -### `srt_epoll_uwait` +#### `srt_epoll_uwait` In this function only the SRT sockets can be subscribed (it reports error if you pass an epoll id that is subscribed to system sockets). @@ -440,7 +425,12 @@ the epoll container. The SRT EPoll system does not supports all features of Linux epoll. For example, it only supports level-triggered events for system sockets. -### Transmission types +## Transmission Types + +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. SRT was originally intended to be used for Live Streaming and therefore its main and default transmission type is "live". However, SRT supports the modes that @@ -527,9 +517,9 @@ lost, or at least not for all still unacknowledged packets. The congestion contr class is responsible for the algorithm for taking care of this situation, which is either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. -### Transmission method: Live +### Transmission Method: Live -Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](APISocketOptions.md): +Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](API-socket-options.md): - `SRTO_TSBPDMODE` = true - `SRTO_RCVLATENCY` = 120 @@ -563,7 +553,7 @@ Otherwise the behavior is undefined and might be surprisingly disappointing. The reading function will always return only a payload that was sent, and it will HANGUP until the time to play has come for this packet (if TSBPD mode is on) or when it is available without gaps of -lost packets (if TSBPD mode is off - see [`SRTO_TSBPDMODE`](APISocketOptions.md#SRTO_TSBPDMODE)). +lost packets (if TSBPD mode is off - see [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE)). You may wish to tweak some of the parameters below: @@ -604,9 +594,9 @@ too long before acknowledging them. This mechanism isn't used (i.e. the BLIND RE situation isn't handled at all) when `SRTO_NAKREPORT` is set by the peer -- the NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. -### Transmission method: Buffer +### Transmission Method: Buffer -Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](APISocketOptions.md): +Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](API-socket-options.md): - `SRTO_TSBPDMODE` = false - `SRTO_RCVLATENCY` = 0 @@ -652,7 +642,7 @@ designate features used in Live mode. None are used with File mode. The only opt that makes sense to modify after the `SRTT_FILE` type was set is `SRTO_MESSAGEAPI`, which is described below. -### Transmission method: Message +### Transmission Method: Message Setting `SRTO_TRANSTYPE` to `SRTT_FILE` and then setting `SRTO_MESSAGEAPI` to `true` implies usage of the Message transmission method. Parameters are set as @@ -708,7 +698,6 @@ Note that you can use any of the sending and receiving functions for sending and receiving messages, except `sendfile/recvfile`, which are dedicated exclusively for Buffer API. -For more information, see [APISocketOptions.md](APISocketOptions.md). +For more information, see [SRT API Socket Options](API-socket-options.md). -[Return to top](#srt-api) - +[Return to Top of Page](#srt-api) diff --git a/docs/statistics.md b/docs/API/statistics.md similarity index 93% rename from docs/statistics.md rename to docs/API/statistics.md index 62a37bcbf..f82b8074c 100644 --- a/docs/statistics.md +++ b/docs/API/statistics.md @@ -23,7 +23,7 @@ The following API functions can be used to retrieve statistics on an SRT socket: * `int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear)` * `int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous)` -Refer to the documentation of the [API functions](API-functions.md) for usage instructions. +Refer to the documentation of the [SRT API Functions](API-functions.md) for usage instructions. ### Summary Table @@ -134,19 +134,19 @@ The time elapsed, in milliseconds, since the SRT socket has been created (after The total number of sent DATA packets, including retransmitted packets ([pktRetransTotal](#pktRetransTotal)). Available for sender. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic counts sent packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) as well. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic counts sent packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) as well. Introduced in SRT v1.4.0. #### pktRecvTotal The total number of received DATA packets, including retransmitted packets ([pktRcvRetransTotal](#pktRcvRetransTotal)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic counts received packet filter control packets ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)) as well. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic counts received packet filter control packets ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)) as well. Introduced in SRT v1.4.0. #### pktSentUniqueTotal The total number of *unique* DATA packets sent by the SRT sender. Available for sender. -This value contains only *unique* *original* DATA packets. Retransmitted DATA packets ([pktRetransTotal](#pktRetransTotal)) are not taken into account. If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](https://cac-word-edit.officeapps.live.com/we/API.md)), packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) are also not taken into account. +This value contains only *unique* *original* DATA packets. Retransmitted DATA packets ([pktRetransTotal](#pktRetransTotal)) are not taken into account. If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) are also not taken into account. This value corresponds to the number of original DATA packets sent by the SRT sender. It counts every packet sent over the network for the first time, and can be calculated as follows: `pktSentUniqueTotal = pktSentTotal ā€“ pktRetransTotal`, or by `pktSentUniqueTotal = pktSentTotal ā€“ pktRetransTotal - pktSndFilterExtraTotal` if the `SRTO_PACKETFILTER` socket option is enabled. The original DATA packets are sent only once. @@ -158,11 +158,11 @@ Unique means "first arrived" DATA packets. There is no difference whether a pack This statistic doesn't count -- duplicate packets (retransmitted or sent several times by defective hardware/software), +- duplicate packets (retransmitted or sent several times by defective hardware/software), - arrived too late packets (retransmitted or original packets arrived out of order) that were already dropped by the TLPKTDROP mechanism (see [pktRcvDropTotal](#pktRcvDropTotal) statistic), - arrived in time packets, but decrypted with errors (see [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic), and, as a result, dropped by the TLPKTDROP mechanism (see [pktRcvDropTotal](#pktRcvDropTotal) statistic). -DATA packets recovered by the packet filter ([pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal)) are taken into account if the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)). Do not mix up with the control packets received by the packet filter ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)). +DATA packets recovered by the packet filter ([pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal)) are taken into account if the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)). Do not mix up with the control packets received by the packet filter ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)). #### pktSndLossTotal @@ -224,11 +224,11 @@ The total accumulated time in microseconds, during which the SRT sender has some The total number of _dropped_ by the SRT sender DATA packets that have no chance to be delivered in time (refer to [TLPKTDROP](https://github.com/Haivision/srt-rfc/blob/master/draft-sharabayko-mops-srt.md#too-late-packet-drop-too-late-packet-drop) mechanism). Available for sender. -Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [API.md](API.md). +Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [SRT API Socket Options](API-socket-options.md). The delay before TLPKTDROP mechanism is triggered is calculated as follows `SRTO_PEERLATENCY + SRTO_SNDDROPDELAY + 2 * interval between sending ACKs`, -where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds an extra to `SRTO_PEERLATENCY` delay, the default `interval between sending ACKs` is 10 milliseconds. The minimum delay is `1000 + 2 * interval between sending ACKs` milliseconds. Refer to `SRTO_PEERLATENCY`, `SRTO_SNDDROPDELAY` socket options in [API.md](API.md). +where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds an extra to `SRTO_PEERLATENCY` delay, the default `interval between sending ACKs` is 10 milliseconds. The minimum delay is `1000 + 2 * interval between sending ACKs` milliseconds. Refer to `SRTO_PEERLATENCY`, `SRTO_SNDDROPDELAY` socket options in [SRT API Socket Options](API-socket-options.md). #### pktRcvDropTotal @@ -238,7 +238,7 @@ This statistic counts - arrived too late packets (retransmitted or original packets arrived out of order), - arrived in time packets, but decrypted with errors (see also [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic). -Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [API.md](API.md). +Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [SRT API Socket Options](API-socket-options.md). #### pktRcvUndecryptTotal @@ -246,31 +246,31 @@ The total number of packets that failed to be decrypted at the receiver side. Av #### pktSndFilterExtraTotal -The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for sender. +The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for sender. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterExtraTotal -The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterSupplyTotal -The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterLossTotal -The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### byteSentTotal @@ -363,25 +363,25 @@ Same as [pktRecvNAKTotal](#pktRecvNAKTotal), but for a specified interval. Same as [pktSndFilterExtraTotal](#pktSndFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterExtra Same as [pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterSupply Same as [pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterLoss Same as [pktRcvFilterLossTotal](#pktRcvFilterLossTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### mbpsSendRate @@ -592,7 +592,7 @@ conditions a nonzero value might be be provided by a congestion control module, although none of the built-in congestion control modules currently use it. -Refer to `SRTO_MAXBW` and `SRTO_INPUTBW` in [API.md](API.md). +Refer to `SRTO_MAXBW` and `SRTO_INPUTBW` in [SRT API Socket Options](API-socket-options.md). #### byteMSS @@ -602,7 +602,7 @@ Should not exceed the size of the maximum transmission unit (MTU), in bytes. Sen The default size of the UDP packet used for transport, including all possible headers (Ethernet, IP and UDP), is 1500 bytes. -Refer to `SRTO_MSS` in [API.md](API.md). +Refer to `SRTO_MSS` in [SRT API Socket Options](API-socket-options.md). #### pktSndBuf @@ -727,9 +727,9 @@ that is received late. SRT group statistics are implemented for SRT Connection Bonding feature and available since SRT v1.5.0. Check the following documentation and code examples for details: -- [Introduction in SRT Connection Bonding feature](https://github.com/Haivision/srt/blob/master/docs/bonding-intro.md), -- [The concept of socket groups](https://github.com/Haivision/srt/blob/master/docs/socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, -- Check also [API](https://github.com/Haivision/srt/blob/master/docs/API.md) and [API functions](https://github.com/Haivision/srt/blob/master/docs/API-functions.md) documentation for Connection Bonding related updates, +- [Introduction in SRT Connection Bonding feature](../bonding-intro.md), +- [The concept of socket groups](../socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, +- Check also [SRT API](API.md) and [SRT API Functions](API-functions.md) documentation for Connection Bonding related updates, - Code examples: simple [client](https://github.com/Haivision/srt/blob/master/examples/test-c-client-bonding.c) and [server](https://github.com/Haivision/srt/blob/master/examples/test-c-server-bonding.c) implementation. `srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [API functions](API-functions.md) for usage instructions. @@ -768,7 +768,7 @@ This value counts every *original* DATA packet sent over the network for the fir This statistic does not count retransmitted DATA packets that are individual per socket connection within the group. See the corresponding [pktRetransTotal](#pktRetransTotal) socket statistic. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic does not count packet filter control packets that are individual per socket connection within the group. See the corresponding [pktSndFilterExtraTotal](#pktSndFilterExtraTotal) socket statistic. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic does not count packet filter control packets that are individual per socket connection within the group. See the corresponding [pktSndFilterExtraTotal](#pktSndFilterExtraTotal) socket statistic. #### pktRecvUniqueTotal diff --git a/docs/AccessControl.md b/docs/AccessControl.md index df35e662c..402f90624 100644 --- a/docs/AccessControl.md +++ b/docs/AccessControl.md @@ -6,7 +6,7 @@ One type of information that can be interchanged when a connection is being established in SRT is "Stream ID", which can be used in a caller-listener connection layout. This is a string of maximum 512 characters set on the caller side. It can be retrieved at the listener side on the newly accepted socket -through a socket option (see `SRTO_STREAMID` in [API.md](API.md)). +through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](API/API-socket-options.md)). As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..ed2ad475c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +# Documentation Overview + +## SRT API Documents + +| Folder Name | File Name | Description | Refer as | +| :---------: | ------------------------------------------------------ | --------------------------------------------------- | ---------------------- | +| API | [API.md](API/API.md) | Detailed description of the SRT C API | SRT API | +| API | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions | SRT API Functions | +| API | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API | SRT API Socket Options | +| API | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics | SRT Statistics | diff --git a/docs/handshake.md b/docs/handshake.md index 5c16d07e7..756c538e4 100644 --- a/docs/handshake.md +++ b/docs/handshake.md @@ -1540,7 +1540,7 @@ application should set it on a Caller socket using the `SRTO_STREAMID` option. Upon connection, the accepted socket on the Listener side will have exactly the same value set, and it can be retrieved using the same option. For more details about the prospective use of this option, please refer to the -[API description document](API.md) and [SRT Access Control guidelines](AccessControl.md). +[SRT API Socket Options](API/API-socket-options.md) and [SRT Access Control guidelines](AccessControl.md). [Return to top of page](#srt-handshake) From e328bec1fb1dd9b3356693f7e175b19ac91fd262 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Tue, 30 Mar 2021 16:47:17 +0800 Subject: [PATCH 107/790] [core] Fix typo of variable name --- srtcore/epoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index c4c31782f..5f4992851 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -608,7 +608,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd { #ifdef LINUX const int max_events = ed.m_sLocals.size(); - SRT_ASSERT(max_event > 0); + SRT_ASSERT(max_events > 0); epoll_event ev[max_events]; int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0); @@ -631,7 +631,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd #elif defined(BSD) || TARGET_OS_MAC struct timespec tmout = {0, 0}; const int max_events = ed.m_sLocals.size(); - SRT_ASSERT(max_event > 0); + SRT_ASSERT(max_events > 0); struct kevent ke[max_events]; int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout); From 50c83555cb7c1dd83cc765271a1a18d9b0e22e6c Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 30 Mar 2021 12:02:38 +0200 Subject: [PATCH 108/790] [core] Fixed wrong limitation on SRTO_FC option. (#1899) Fixed documentation. --- docs/API/API-socket-options.md | 2 +- srtcore/socketconfig.h | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2dfc3bd8f..a77b97cc2 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -394,7 +394,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | | `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -Flight Flag Size (maximum number of bytes that can be sent without +Flight Flag Size (maximum number of packets that can be sent without being acknowledged) [Return to list](#list-of-options) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index c41bf4caf..2b0c3943e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -185,7 +185,7 @@ struct CSrtConfig: CSrtMuxerConfig static const uint32_t COMM_DEF_STABILITY_TIMEOUT_US = 80 * 1000; // Mimimum recv flight flag size is 32 packets - static const int DEF_MAX_FLIGHT_PKT = 32; + static const int DEF_MIN_FLIGHT_PKT = 32; static const size_t MAX_SID_LENGTH = 512; static const size_t MAX_PFILTER_LENGTH = 64; static const size_t MAX_CONG_LENGTH = 16; @@ -418,7 +418,7 @@ struct CSrtConfigSetter if (fc < 1) throw CUDTException(MJ_NOTSUP, MN_INVAL); - co.iFlightFlagSize = std::min(fc, +co.DEF_MAX_FLIGHT_PKT); + co.iFlightFlagSize = std::max(fc, +co.DEF_MIN_FLIGHT_PKT); } }; @@ -447,11 +447,10 @@ struct CSrtConfigSetter // Mimimum recv buffer size is 32 packets const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; - // XXX This magic 32 deserves some constant - if (val > mssin_size * co.DEF_MAX_FLIGHT_PKT) + if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; else - co.iRcvBufSize = co.DEF_MAX_FLIGHT_PKT; + co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; // recv buffer MUST not be greater than FC size if (co.iRcvBufSize > co.iFlightFlagSize) From b4288ce503dcdeb26d5388fad19281cbd26a8eb5 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 30 Mar 2021 14:58:55 +0200 Subject: [PATCH 109/790] [doc] Removed misleading information about connect callback (#1882) in successful connection case --- docs/API/API-functions.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 9ed45dc7b..734ce6221 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -942,12 +942,6 @@ resolved. When all links fail, you will only get a general error code for the group. This mechanism allows you to get individual errors for particular member connection failures. -You can also use this mechanism as an alternative method for a single-socket -connection in non-blocking mode to trigger an action when the connection -process is finished. It is recommended, however, that you use this callback -only to collect failure information, as the call will happen in one of the -internal SRT threads. - **Arguments**: * [`u`](#u): Socket or group that will be used for connecting and for which the hook is installed From 26c07c547ec6c456e82e04aea0cb932d03e72f4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 10:25:11 +0200 Subject: [PATCH 110/790] [core] Made CEpoll::m_EPollLock mutable --- srtcore/epoll.cpp | 2 +- srtcore/epoll.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 5f4992851..71945bdbd 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -808,7 +808,7 @@ int CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut, boo return 0; } -bool CEPoll::empty(CEPollDesc& d) +bool CEPoll::empty(const CEPollDesc& d) const { ScopedLock lg (m_EPollLock); return d.watch_empty(); diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 23b6c0065..3786137d5 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -425,7 +425,7 @@ friend class CRendezvousQueue; int swait(CEPollDesc& d, fmap_t& st, int64_t msTimeOut, bool report_by_exception = true); /// Empty subscription check - for internal use only. - bool empty(CEPollDesc& d); + bool empty(const CEPollDesc& d) const; /// Reports which events are ready on the given socket. /// @param mp socket event map retirned by `swait` @@ -486,7 +486,7 @@ friend class CRendezvousQueue; srt::sync::Mutex m_SeedLock; std::map m_mPolls; // all epolls - srt::sync::Mutex m_EPollLock; + mutable srt::sync::Mutex m_EPollLock; }; #if ENABLE_HEAVY_LOGGING From 262fe21b79e8a3d5d997d969611b7e0a8ff1f958 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 10:25:43 +0200 Subject: [PATCH 111/790] [core] sendBackup: logical OR instead of bitwise --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b13caad08..50b4cb6ca 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4193,7 +4193,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // is performed, and this one will result in none-write-ready, this will // be fixed just after returning from this function. - ready_again = ready_again | d->ps->writeReady(); + ready_again = ready_again || d->ps->writeReady(); } w_mc.grpdata_size = i; From a499c42356f86533e5d79a7c37395c51e2565943 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 16:58:02 +0200 Subject: [PATCH 112/790] [core] SocketData moved to group_common.h (#1907) (Refactoring) --- srtcore/api.cpp | 6 ++-- srtcore/api.h | 4 +-- srtcore/core.cpp | 13 +++++---- srtcore/filelist.maf | 2 ++ srtcore/group.cpp | 35 ----------------------- srtcore/group.h | 24 ++-------------- srtcore/group_common.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ srtcore/group_common.h | 60 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 138 insertions(+), 68 deletions(-) create mode 100644 srtcore/group_common.cpp create mode 100644 srtcore/group_common.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4293d8021..3b8f81ef1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1417,7 +1417,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar // Do it after setting all stored options, as some of them may // influence some group data. - CUDTGroup::SocketData data = g.prepareData(ns); + srt::groups::SocketData data = srt::groups::prepareSocketData(ns); if (targets[tii].token != -1) { // Reuse the token, if specified by the caller @@ -3228,7 +3228,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Check if the socket already is in the group - CUDTGroup::SocketData* f; + srt::groups::SocketData* f; if (g->contains(socket, (f))) { // XXX This is internal error. Report it, but continue @@ -3237,7 +3237,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) s->m_GroupOf = g; return 0; } - s->m_GroupMemberData = g->add(g->prepareData(s)); + s->m_GroupMemberData = g->add(srt::groups::prepareSocketData(s)); s->m_GroupOf = g; return 0; diff --git a/srtcore/api.h b/srtcore/api.h index 35f215ca3..cd467a978 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -117,8 +117,8 @@ class CUDTSocket SRTSOCKET m_PeerID; //< peer socket ID #if ENABLE_EXPERIMENTAL_BONDING - CUDTGroup::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member - CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't + srt::groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member + CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't #endif int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 5a1c62209..fc204816d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -79,6 +79,7 @@ modified by #endif using namespace std; +using namespace srt; using namespace srt::sync; using namespace srt_logging; @@ -3020,7 +3021,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } - CUDTGroup::SocketData* f = m_parent->m_GroupMemberData; + srt::groups::SocketData* f = m_parent->m_GroupMemberData; f->weight = link_weight; f->agent = m_parent->m_SelfAddr; @@ -3119,7 +3120,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l // Copy of addSocketToGroup. No idea how many parts could be common, not much. // Check if the socket already is in the group - CUDTGroup::SocketData* f; + srt::groups::SocketData* f; if (gp->contains(m_SocketID, (f))) { // XXX This is internal error. Report it, but continue @@ -3130,7 +3131,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l return 0; } - s->m_GroupMemberData = gp->add(gp->prepareData(s)); + s->m_GroupMemberData = gp->add(groups::prepareSocketData(s)); s->m_GroupOf = gp; // Record the remote address in the group data. @@ -4550,7 +4551,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE"); - CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = m_parent->m_GroupMemberData; gi->sndstate = SRT_GST_IDLE; gi->rcvstate = SRT_GST_IDLE; gi->laststatus = SRTS_CONNECTED; @@ -9294,7 +9295,7 @@ int CUDT::processData(CUnit* in_unit) if (m_parent->m_GroupOf) { ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); - CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long @@ -9793,7 +9794,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) if (self->m_parent->m_GroupOf) { - CUDTGroup::SocketData* gi = self->m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = self->m_parent->m_GroupMemberData; if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index bdd57e789..4064c265f 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -28,6 +28,7 @@ sync.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp +group_common.cpp SOURCES - !ENABLE_STDCXX_SYNC sync_posix.cpp @@ -73,3 +74,4 @@ window.h PRIVATE HEADERS - ENABLE_EXPERIMENTAL_BONDING group.h +group_common.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 50b4cb6ca..4c8feff31 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -244,41 +244,6 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) return &*end; } -CUDTGroup::SocketData CUDTGroup::prepareData(CUDTSocket* s) -{ - // This uses default SRT_GST_BROKEN because when the group operation is done, - // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is - // recognized as an initial state of the fresh added socket to the group, - // so some "initial configuration" must be done on it, after which it's - // turned into SRT_GST_RUNNING, that is, it's treated as all others. When - // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned - // up, however, unless the status is simultaneously SRTS_BROKEN. - - // The order of operations is then: - // - add the socket to the group in this "broken" initial state - // - connect the socket (or get it extracted from accept) - // - update the socket state (should be SRTS_CONNECTED) - // - once the connection is established (may take time with connect), set SRT_GST_IDLE - // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING - SocketData sd = { - s->m_SocketID, - s, - -1, - SRTS_INIT, - SRT_GST_BROKEN, - SRT_GST_BROKEN, - -1, - -1, - sockaddr_any(), - sockaddr_any(), - false, - false, - false, - 0 // weight - }; - return sd; -} - CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_pGlobal(&CUDT::s_UDTUnited) , m_GroupID(-1) diff --git a/srtcore/group.h b/srtcore/group.h index 4f014a849..1477a4583 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -19,6 +19,7 @@ Written by #include "srt.h" #include "common.h" #include "packet.h" +#include "group_common.h" #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; @@ -31,6 +32,7 @@ class CUDTGroup typedef srt::sync::steady_clock::time_point time_point; typedef srt::sync::steady_clock::duration duration; typedef srt::sync::steady_clock steady_clock; + typedef srt::groups::SocketData SocketData; public: typedef SRT_MEMBERSTATUS GroupState; @@ -57,26 +59,6 @@ class CUDTGroup static int32_t s_tokenGen; static int32_t genToken() { ++s_tokenGen; if (s_tokenGen < 0) s_tokenGen = 0; return s_tokenGen;} - struct SocketData - { - SRTSOCKET id; - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; - }; - struct ConfigItem { SRT_SOCKOPT so; @@ -125,8 +107,6 @@ class CUDTGroup CUDTGroup(SRT_GROUP_TYPE); ~CUDTGroup(); - static SocketData prepareData(CUDTSocket* s); - SocketData* add(SocketData data); struct HaveID diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp new file mode 100644 index 000000000..513e5065c --- /dev/null +++ b/srtcore/group_common.cpp @@ -0,0 +1,62 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#include "platform_sys.h" + +#include "group_common.h" +#include "api.h" + +namespace srt +{ + namespace groups + { + + SocketData prepareSocketData(CUDTSocket* s) + { + // This uses default SRT_GST_BROKEN because when the group operation is done, + // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is + // recognized as an initial state of the fresh added socket to the group, + // so some "initial configuration" must be done on it, after which it's + // turned into SRT_GST_RUNNING, that is, it's treated as all others. When + // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned + // up, however, unless the status is simultaneously SRTS_BROKEN. + + // The order of operations is then: + // - add the socket to the group in this "broken" initial state + // - connect the socket (or get it extracted from accept) + // - update the socket state (should be SRTS_CONNECTED) + // - once the connection is established (may take time with connect), set SRT_GST_IDLE + // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING + SocketData sd = { + s->m_SocketID, + s, + -1, + SRTS_INIT, + SRT_GST_BROKEN, + SRT_GST_BROKEN, + -1, + -1, + sockaddr_any(), + sockaddr_any(), + false, + false, + false, + 0 // weight + }; + return sd; + } + + } // namespace groups +} // namespace srt diff --git a/srtcore/group_common.h b/srtcore/group_common.h new file mode 100644 index 000000000..13ef4ad4a --- /dev/null +++ b/srtcore/group_common.h @@ -0,0 +1,60 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#ifndef INC_SRT_GROUP_COMMON_H +#define INC_SRT_GROUP_COMMON_H + +#include "srt.h" +#include "common.h" +#include "core.h" + +#include + +namespace srt +{ + namespace groups + { + typedef SRT_MEMBERSTATUS GroupState; + + struct SocketData + { + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; + }; + + SocketData prepareSocketData(CUDTSocket* s); + + typedef std::list group_t; + typedef group_t::iterator gli_t; + + } // namespace groups +} // namespace srt + + +#endif // INC_SRT_GROUP_COMMON_H From 9cbf82e921b96e5c9372415e6888f5b836d750b1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 11:18:02 +0200 Subject: [PATCH 113/790] [core] Added SRT_STATIC_ASSERT macro --- srtcore/common.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/srtcore/common.h b/srtcore/common.h index 32be66179..6c9948351 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -78,7 +78,6 @@ modified by #define NET_ERROR WSAGetLastError() #endif - #ifdef _DEBUG #include #define SRT_ASSERT(cond) assert(cond) @@ -86,6 +85,12 @@ modified by #define SRT_ASSERT(cond) #endif +#if HAVE_FULL_CXX11 +#define SRT_STATIC_ASSERT(cond, msg) static_assert(cond, msg) +#else +#define SRT_STATIC_ASSERT(cond, msg) +#endif + #include // Class CUDTException exposed for C++ API. From 60ae6e56014b5ee48c8e25eda4d7fcc2e28f79cc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 17:33:13 +0200 Subject: [PATCH 114/790] [apps] Added CSV and JSON stats byteAvailSndBuf, msSndBuf, byteAvailRcvBuf, msRcvBuf, msRcvTsbPdDelay --- apps/apputil.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 7d01bd9c4..0afe40cd5 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -391,10 +391,10 @@ struct SrtStatsTableInit STAT(SEND, bytes, byteSent); STAT(SEND, bytesUnique, byteSentUnique); STAT(SEND, bytesDropped, byteSndDrop); + STAT(SEND, byteAvailBuf, byteAvailSndBuf); + STAT(SEND, msBuf, msSndBuf); STAT(SEND, mbitRate, mbpsSendRate); STAT(SEND, sendPeriod, usPktSndPeriod); - //STAT(SEND, msAvgResponseTime, msAvgResponseTime); - //STAT(SEND, msMaxResponseTime, msMaxResponseTime); STAT(RECV, packets, pktRecv); STAT(RECV, packetsUnique, pktRecvUnique); @@ -409,7 +409,10 @@ struct SrtStatsTableInit STAT(RECV, bytesUnique, byteRecvUnique); STAT(RECV, bytesLost, byteRcvLoss); STAT(RECV, bytesDropped, byteRcvDrop); + STAT(RECV, byteAvailBuf, byteAvailRcvBuf); + STAT(RECV, msBuf, msRcvBuf); STAT(RECV, mbitRate, mbpsRecvRate); + STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); } } g_SrtStatsTableInit (g_SrtStatsTable); @@ -586,12 +589,8 @@ class SrtStatsCsv : public SrtStatsWriter output << endl; first_line_printed = true; } - int rcv_latency = 0; - int int_len = sizeof rcv_latency; - srt_getsockopt(sid, 0, SRTO_RCVLATENCY, &rcv_latency, &int_len); // Values - #ifdef HAS_PUT_TIME // HDR: Timepoint output << print_timestamp() << ","; @@ -685,4 +684,3 @@ SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) return SRTSTATS_PROFMAT_INVALID; } - From d5458908554b7ca6d5d2bc01b94a267a82ae62e9 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 6 Apr 2021 16:15:31 +0800 Subject: [PATCH 115/790] [docs] Fixed typos in LowLevelInfo (#1914) --- docs/LowLevelInfo.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/LowLevelInfo.md b/docs/LowLevelInfo.md index b3793dbf5..f7d1c1217 100644 --- a/docs/LowLevelInfo.md +++ b/docs/LowLevelInfo.md @@ -2,9 +2,9 @@ ## Introduction -This documnent will contain loose information for various topis referring to -the SRT source code describing some cross-source analysis that would be -unobvious for a source code reviewer. It's not a complete documentation of +This document contains information on various topics related to +the SRT source code, including descriptions of some cross-source analysis that would +not be obvious for a source code reviewer. It's not a complete documentation of anything, rather a collection of various kind of information retrieved during development and even reverse engineering. @@ -236,4 +236,3 @@ CSndQueue::worker CUDT::packData ``` - From f31f1fbae6c52c7dd2a0368cf8d4afdf0e1ab026 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 17:45:08 +0200 Subject: [PATCH 116/790] [core] Applied clang-format on buffer.h. Fixed Doxygen comments alignment --- srtcore/buffer.h | 839 ++++++++++++++++++++++------------------------- 1 file changed, 397 insertions(+), 442 deletions(-) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 3963d3432..f0a499fd4 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -53,7 +53,6 @@ modified by #ifndef INC_SRT_BUFFER_H #define INC_SRT_BUFFER_H - #include "udt.h" #include "list.h" #include "queue.h" @@ -82,7 +81,8 @@ class AvgBufSize : m_dBytesCountMAvg(0.0) , m_dCountMAvg(0.0) , m_dTimespanMAvg(0.0) - { } + { + } public: bool isTimeToUpdate(const time_point& now) const; @@ -100,531 +100,486 @@ class AvgBufSize double m_dTimespanMAvg; }; - class CSndBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef srt::sync::steady_clock::time_point time_point; + typedef srt::sync::steady_clock::duration duration; public: + // XXX There's currently no way to access the socket ID set for + // whatever the buffer is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the buffer is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - CSndBuffer(int size = 32, int mss = 1500); - ~CSndBuffer(); + CSndBuffer(int size = 32, int mss = 1500); + ~CSndBuffer(); public: - - /// Insert a user buffer into the sending list. - /// For @a w_mctrl the following fields are used: - /// INPUT: - /// - msgttl: timeout for retransmitting the message, if lost - /// - inorder: request to deliver the message in order of sending - /// - srctime: local time as a base for packet's timestamp (0 if unused) - /// - pktseq: sequence number to be stamped on the packet (-1 if unused) - /// - msgno: message number to be stamped on the packet (-1 if unused) - /// OUTPUT: - /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) - /// - pktseq: sequence number to be stamped on the next packet - /// - msgno: message number stamped on the packet - /// @param [in] data pointer to the user data block. - /// @param [in] len size of the block. - /// @param [inout] w_mctrl Message control data - void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); - - /// Read a block of data from file and insert it into the sending list. - /// @param [in] ifs input file stream. - /// @param [in] len size of the block. - /// @return actual size of data added from the file. - - int addBufferFromFile(std::fstream& ifs, int len); - - /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] data the pointer to the data position. - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [in] kflags Odd|Even crypto key flag - /// @return Actual length of data read. - - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); - - /// Find data position to pack a DATA packet for a retransmission. - /// @param [out] data the pointer to the data position. - /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read. - - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); - - /// 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) - /// - /// @return Last time of the last retransmission event for the corresponding DATA packet. - - time_point getPacketRexmitTime(const int offset); - - /// Update the ACK point and may release/unmap/return the user data according to the flag. - /// @param [in] offset number of packets acknowledged. - - int32_t getMsgNoAt(const int offset); - - void ackData(int offset); - - /// Read size of data still in the sending list. - /// @return Current size of the data in the sending list. - - int getCurrBufSize() const; - - int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); - - void updAvgBufSize(const time_point& time); - int getAvgBufSize(int& bytes, int& timespan); - int getCurrBufSize(int& bytes, int& timespan); - - uint64_t getInRatePeriod() const { return m_InRatePeriod; } - - /// Retrieve input bitrate in bytes per second - int getInputRate() const { return m_iInRateBps; } - - /// Update input rate calculation. - /// @param [in] time current time in microseconds - /// @param [in] pkts number of packets newly added to the buffer - /// @param [in] bytes number of payload bytes in those newly added packets - /// - /// @return Current size of the data in the sending list. - void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); - - - void resetInputRateSmpPeriod(bool disable = false) - { - setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); - } + /// Insert a user buffer into the sending list. + /// For @a w_mctrl the following fields are used: + /// INPUT: + /// - msgttl: timeout for retransmitting the message, if lost + /// - inorder: request to deliver the message in order of sending + /// - srctime: local time as a base for packet's timestamp (0 if unused) + /// - pktseq: sequence number to be stamped on the packet (-1 if unused) + /// - msgno: message number to be stamped on the packet (-1 if unused) + /// OUTPUT: + /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) + /// - pktseq: sequence number to be stamped on the next packet + /// - msgno: message number stamped on the packet + /// @param [in] data pointer to the user data block. + /// @param [in] len size of the block. + /// @param [inout] w_mctrl Message control data + void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); + + /// Read a block of data from file and insert it into the sending list. + /// @param [in] ifs input file stream. + /// @param [in] len size of the block. + /// @return actual size of data added from the file. + int addBufferFromFile(std::fstream& ifs, int len); + + /// Find data position to pack a DATA packet from the furthest reading point. + /// @param [out] data the pointer to the data position. + /// @param [out] msgno message number of the packet. + /// @param [out] origintime origin time stamp of the message + /// @param [in] kflags Odd|Even crypto key flag + /// @return Actual length of data read. + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + + /// Find data position to pack a DATA packet for a retransmission. + /// @param [out] data the pointer to the data position. + /// @param [in] offset offset from the last ACK point (backward sequence number difference) + /// @param [out] msgno message number of the packet. + /// @param [out] origintime origin time stamp of the message + /// @param [out] msglen length of the message + /// @return Actual length of data read. + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + + /// 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) + /// + /// @return Last time of the last retransmission event for the corresponding DATA packet. + time_point getPacketRexmitTime(const int offset); + + /// Update the ACK point and may release/unmap/return the user data according to the flag. + /// @param [in] offset number of packets acknowledged. + int32_t getMsgNoAt(const int offset); + + void ackData(int offset); + + /// Read size of data still in the sending list. + /// @return Current size of the data in the sending list. + int getCurrBufSize() const; + + int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + + void updAvgBufSize(const time_point& time); + int getAvgBufSize(int& bytes, int& timespan); + int getCurrBufSize(int& bytes, int& timespan); + + uint64_t getInRatePeriod() const { return m_InRatePeriod; } + + /// Retrieve input bitrate in bytes per second + int getInputRate() const { return m_iInRateBps; } + + /// Update input rate calculation. + /// @param [in] time current time in microseconds + /// @param [in] pkts number of packets newly added to the buffer + /// @param [in] bytes number of payload bytes in those newly added packets + /// + /// @return Current size of the data in the sending list. + void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); + + void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); } private: - void increase(); - void setInputRateSmpPeriod(int period); - - struct Block; // Defined below - static time_point getSourceTime(const CSndBuffer::Block& block); + void increase(); + void setInputRateSmpPeriod(int period); -private: // Constants + struct Block; // Defined below + static time_point getSourceTime(const CSndBuffer::Block& block); - static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms - static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms - static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload +private: // Constants + static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms + static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms + static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - srt::sync::Mutex m_BufLock; // used to synchronize buffer operation + srt::sync::Mutex m_BufLock; // used to synchronize buffer operation - struct Block - { - char* m_pcData; // pointer to the data block - int m_iLength; // length of the block + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // length of the block - int32_t m_iMsgNoBitset; // message number - int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // original request time - time_point m_tsRexmitTime; // packet retransmission time - uint64_t m_llSourceTime_us; - int m_iTTL; // time to live (milliseconds) + int32_t m_iMsgNoBitset; // message number + int32_t m_iSeqNo; // sequence number for scheduling + time_point m_tsOriginTime; // original request time + time_point m_tsRexmitTime; // packet retransmission time + uint64_t m_llSourceTime_us; + int m_iTTL; // time to live (milliseconds) - Block* m_pNext; // next block + Block* m_pNext; // next block - int32_t getMsgSeq() - { - // NOTE: this extracts message ID with regard to REXMIT flag. - // This is valid only for message ID that IS GENERATED in this instance, - // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter - // for the peer that it uses LESS bits to represent the message. - return m_iMsgNoBitset & MSGNO_SEQ::mask; - } + int32_t getMsgSeq() + { + // NOTE: this extracts message ID with regard to REXMIT flag. + // This is valid only for message ID that IS GENERATED in this instance, + // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter + // for the peer that it uses LESS bits to represent the message. + return m_iMsgNoBitset & MSGNO_SEQ::mask; + } - } *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; + } * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; - // m_pBlock: The head pointer - // m_pFirstBlock: The first block - // m_pCurrBlock: The current block - // m_pLastBlock: The last block (if first == last, buffer is empty) + // m_pBlock: The head pointer + // m_pFirstBlock: The first block + // m_pCurrBlock: The current block + // m_pLastBlock: The last block (if first == last, buffer is empty) - struct Buffer - { - char* m_pcData; // buffer - int m_iSize; // size - Buffer* m_pNext; // next buffer - } *m_pBuffer; // physical buffer + struct Buffer + { + char* m_pcData; // buffer + int m_iSize; // size + Buffer* m_pNext; // next buffer + } * m_pBuffer; // physical buffer - int32_t m_iNextMsgNo; // next message number + int32_t m_iNextMsgNo; // next message number - int m_iSize; // buffer size (number of packets) - int m_iMSS; // maximum seqment/packet size + int m_iSize; // buffer size (number of packets) + int m_iMSS; // maximum seqment/packet size - int m_iCount; // number of used blocks + int m_iCount; // number of used blocks - int m_iBytesCount; // number of payload bytes in queue - time_point m_tsLastOriginTime; + int m_iBytesCount; // number of payload bytes in queue + time_point m_tsLastOriginTime; - AvgBufSize m_mavg; + AvgBufSize m_mavg; - int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime - int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime - time_point m_tsInRateStartTime; - uint64_t m_InRatePeriod; // usec - int m_iInRateBps; // Input Rate in Bytes/sec - int m_iAvgPayloadSz; // Average packet payload size + int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime + int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime + time_point m_tsInRateStartTime; + uint64_t m_InRatePeriod; // usec + int m_iInRateBps; // Input Rate in Bytes/sec + int m_iAvgPayloadSz; // Average packet payload size private: - CSndBuffer(const CSndBuffer&); - CSndBuffer& operator=(const CSndBuffer&); + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); }; //////////////////////////////////////////////////////////////////////////////// - class CRcvBuffer { typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef srt::sync::steady_clock::duration duration; public: - // XXX There's currently no way to access the socket ID set for // whatever the queue is currently working for. Required to find // some way to do this, possibly by having a "reverse pointer". // Currently just "unimplemented". std::string CONID() const { return ""; } - static const int DEFAULT_SIZE = 65536; - /// Construct the buffer. - /// @param [in] queue CUnitQueue that actually holds the units (packets) - /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); - ~CRcvBuffer(); - + static const int DEFAULT_SIZE = 65536; + /// Construct the buffer. + /// @param [in] queue CUnitQueue that actually holds the units (packets) + /// @param [in] bufsize_pkts in units (packets) + CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + ~CRcvBuffer(); public: - - /// Write data into the buffer. - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. - /// @return 0 is success, -1 if data is repeated. - - int addData(CUnit* unit, int offset); - - /// Read data into a user buffer. - /// @param [in] data pointer to user buffer. - /// @param [in] len length of user buffer. - /// @return size of data read. - - int readBuffer(char* data, int len); - - /// Read data directly into file. - /// @param [in] file C++ file stream. - /// @param [in] len expected length of data to write into the file. - /// @return size of data read. - - int readBufferToFile(std::fstream& ofs, int len); - - /// Update the ACK point of the buffer. - /// @param [in] len number of units to be acknowledged. - /// @return 1 if a user buffer is fulfilled, otherwise 0. - - int ackData(int len); - - /// Query how many buffer space left for data receiving. - /// Actually only acknowledged packets, that are still in the buffer, - /// are considered to take buffer space. - /// - /// @return size of available buffer space (including user buffer) for data receiving. - /// Not counting unacknowledged packets. - - int getAvailBufSize() const; - - /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). - /// @return size of valid (continous) data for reading. - - int getRcvDataSize() const; - - /// Query how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - - int getRcvDataSize(int& bytes, int &spantime); - - /// Query a 1 sec moving average of how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - - int getRcvAvgDataSize(int& bytes, int& spantime); - - /// Query how many data of the receive buffer is acknowledged. - /// @param [in] now current time in us. - /// @return none. - - void updRcvAvgDataSize(const time_point& now); - - /// Query the received average payload size. - /// @return size (bytes) of payload size - - unsigned getRcvAvgPayloadSize() const; - - - /// Mark the message to be dropped from the message list. - /// @param [in] msgno message number. - /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the msgno value) - - void dropMsg(int32_t msgno, bool using_rexmit_flag); - - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @return actuall size of data read. - - int readMsg(char* data, int len); + /// Write data into the buffer. + /// @param [in] unit pointer to a data unit containing new packet + /// @param [in] offset offset from last ACK point. + /// @return 0 is success, -1 if data is repeated. + int addData(CUnit* unit, int offset); + + /// Read data into a user buffer. + /// @param [in] data pointer to user buffer. + /// @param [in] len length of user buffer. + /// @return size of data read. + int readBuffer(char* data, int len); + + /// Read data directly into file. + /// @param [in] file C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. + int readBufferToFile(std::fstream& ofs, int len); + + /// Update the ACK point of the buffer. + /// @param [in] len number of units to be acknowledged. + /// @return 1 if a user buffer is fulfilled, otherwise 0. + int ackData(int len); + + /// Query how many buffer space left for data receiving. + /// Actually only acknowledged packets, that are still in the buffer, + /// are considered to take buffer space. + /// + /// @return size of available buffer space (including user buffer) for data receiving. + /// Not counting unacknowledged packets. + int getAvailBufSize() const; + + /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). + /// @return size of valid (continous) data for reading. + int getRcvDataSize() const; + + /// Query how many data was received and acknowledged. + /// @param [out] bytes bytes + /// @param [out] spantime spantime + /// @return size in pkts of acked data. + int getRcvDataSize(int& bytes, int& spantime); + + /// Query a 1 sec moving average of how many data was received and acknowledged. + /// @param [out] bytes bytes + /// @param [out] spantime spantime + /// @return size in pkts of acked data. + int getRcvAvgDataSize(int& bytes, int& spantime); + + /// Query how many data of the receive buffer is acknowledged. + /// @param [in] now current time in us. + /// @return none. + void updRcvAvgDataSize(const time_point& now); + + /// Query the received average payload size. + /// @return size (bytes) of payload size + unsigned getRcvAvgPayloadSize() const; + + /// Mark the message to be dropped from the message list. + /// @param [in] msgno message number. + /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the + /// msgno value) + void dropMsg(int32_t msgno, bool using_rexmit_flag); + + /// read a message. + /// @param [out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @return actuall size of data read. + int readMsg(char* data, int len); #if ENABLE_HEAVY_LOGGING - void readMsgHeavyLogging(int p); + void readMsgHeavyLogging(int p); #endif - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// @return actuall size of data read. - - int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto); - /// Query if data is ready to read (tsbpdtime <= now if TsbPD is active). - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// of next packet in recv buffer, ready or not. - /// @param [out] curpktseq Sequence number of the packet if there is one ready to play - /// @return true if ready to play, false otherwise (tsbpdtime may be !0 in - /// both cases). - - bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); + /// read a message. + /// @param [out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay + /// @return actuall size of data read. + int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto); + + /// Query if data is ready to read (tsbpdtime <= now if TsbPD is active). + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay + /// of next packet in recv buffer, ready or not. + /// @param [out] curpktseq Sequence number of the packet if there is one ready to play + /// @return true if ready to play, false otherwise (tsbpdtime may be !0 in + /// both cases). + bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); #ifdef SRT_DEBUG_TSBPD_OUTJITTER - void debugTraceJitter(int64_t); + void debugTraceJitter(int64_t); #else - void debugTraceJitter(int64_t) {} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - - bool isRcvDataReady(); - bool isRcvDataAvailable() - { - return m_iLastAckPos != m_iStartPos; - } - CPacket* getRcvReadyPacket(int32_t seqdistance); - - /// Set TimeStamp-Based Packet Delivery Rx Mode - /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay - /// @param [in] delay aggreed TsbPD delay - /// @return 0 + void debugTraceJitter(int64_t) {} +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - int setRcvTsbPdMode(const time_point& timebase, const duration& delay); + bool isRcvDataReady(); + bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } + CPacket* getRcvReadyPacket(int32_t seqdistance); - /// Add packet timestamp for drift caclculation and compensation - /// @param [in] timestamp packet time stamp - /// @param [ref] lock Mutex that should be locked for the operation + /// Set TimeStamp-Based Packet Delivery Rx Mode + /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay + /// @param [in] delay aggreed TsbPD delay + /// @return 0 + int setRcvTsbPdMode(const time_point& timebase, const duration& delay); - bool addRcvTsbPdDriftSample(uint32_t timestamp, srt::sync::Mutex& mutex_to_lock, - duration& w_udrift, time_point& w_newtimebase); + /// Add packet timestamp for drift caclculation and compensation + /// @param [in] timestamp packet time stamp + /// @param [ref] lock Mutex that should be locked for the operation + bool addRcvTsbPdDriftSample(uint32_t timestamp, + srt::sync::Mutex& mutex_to_lock, + duration& w_udrift, + time_point& w_newtimebase); #ifdef SRT_DEBUG_TSBPD_DRIFT - void printDriftHistogram(int64_t iDrift); - void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg); + void printDriftHistogram(int64_t iDrift); + void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg); #endif - /// Get information on the 1st message in queue. - // Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none - /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) - /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets. - /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true - /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: - /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; - /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. - - - bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); - - /// Update the ACK point of the buffer. - /// @param [in] len size of data to be skip & acknowledged. - - void skipData(int len); + /// Get information on the 1st message in queue. + // Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 + /// if none + /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) + /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by + /// missing packets. + /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true + /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: + /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; + /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. + bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); + + /// Update the ACK point of the buffer. + /// @param [in] len size of data to be skip & acknowledged. + void skipData(int len); #if ENABLE_HEAVY_LOGGING - void reportBufferStats() const; // Heavy logging Debug only + void reportBufferStats() const; // Heavy logging Debug only #endif - bool empty() const - { - // This will not always return the intended value, - // that is, it may return false when the buffer really is - // empty - but it will return true then in one of next calls. - // This function will be always called again at some point - // if it returned false, and on true the connection - // is going to be broken - so this behavior is acceptable. - return m_iStartPos == m_iLastAckPos; - } - bool full() const { return m_iStartPos == (m_iLastAckPos+1)%m_iSize; } - int capacity() const { return m_iSize; } - + bool empty() const + { + // This will not always return the intended value, + // that is, it may return false when the buffer really is + // empty - but it will return true then in one of next calls. + // This function will be always called again at some point + // if it returned false, and on true the connection + // is going to be broken - so this behavior is acceptable. + return m_iStartPos == m_iLastAckPos; + } + bool full() const { return m_iStartPos == (m_iLastAckPos + 1) % m_iSize; } + int capacity() const { return m_iSize; } private: - /// This gives up unit at index p. The unit is given back to the - /// free unit storage for further assignment for the new incoming - /// data. - size_t freeUnitAt(size_t p) - { - CUnit* u = m_pUnit[p]; - m_pUnit[p] = NULL; - size_t rmbytes = u->m_Packet.getLength(); - m_pUnitQueue->makeUnitFree(u); - return rmbytes; - } - - /// Adjust receive queue to 1st ready to play message (tsbpdtime < now). - // Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none - /// @retval true 1st packet ready to play without discontinuity (no hole) - /// @retval false tsbpdtime = 0: no packet ready to play - - - bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); + /// This gives up unit at index p. The unit is given back to the + /// free unit storage for further assignment for the new incoming + /// data. + size_t freeUnitAt(size_t p) + { + CUnit* u = m_pUnit[p]; + m_pUnit[p] = NULL; + size_t rmbytes = u->m_Packet.getLength(); + m_pUnitQueue->makeUnitFree(u); + return rmbytes; + } + + /// Adjust receive queue to 1st ready to play message (tsbpdtime < now). + /// Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if + /// none + /// @retval true 1st packet ready to play without discontinuity (no hole) + /// @retval false tsbpdtime = 0: no packet ready to play + bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: + /// Get packet delivery local time base (adjusted for wrap around) + /// (Exposed as used publicly in logs) + /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min + /// @return local delivery time (usec) + time_point getTsbPdTimeBase(uint32_t timestamp_us); - // (This is exposed as used publicly in logs) - /// Get packet delivery local time base (adjusted for wrap around) - /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min - /// @return local delivery time (usec) - - time_point getTsbPdTimeBase(uint32_t timestamp_us); - - int64_t getDrift() const { return m_DriftTracer.drift(); } + int64_t getDrift() const { return m_DriftTracer.drift(); } public: - int32_t getTopMsgno() const; + int32_t getTopMsgno() const; - // @return Wrap check value - bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); + // @return Wrap check value + bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); - void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); - void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); - time_point getPktTsbPdTime(uint32_t timestamp); - int debugGetSize() const; - time_point debugGetDeliveryTime(int offset); + void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); + void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); + time_point getPktTsbPdTime(uint32_t timestamp); + int debugGetSize() const; + time_point debugGetDeliveryTime(int offset); - size_t dropData(int len); + size_t dropData(int len); private: - int extractData(char *data, int len, int p, int q, bool passack); - bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto); + int extractData(char* data, int len, int p, int q, bool passack); + bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto); - /// Describes the state of the first N packets - std::string debugTimeState(size_t first_n_pkts) const; - - /// thread safe bytes counter of the Recv & Ack buffer - /// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true) - /// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer. - /// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer + /// Describes the state of the first N packets + std::string debugTimeState(size_t first_n_pkts) const; - void countBytes(int pkts, int bytes, bool acked = false); + /// thread safe bytes counter of the Recv & Ack buffer + /// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true) + /// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer. + /// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer + void countBytes(int pkts, int bytes, bool acked = false); private: - bool scanMsg(int& w_start, int& w_end, bool& w_passack); - - int shift(int basepos, int shift) const - { - return (basepos + shift) % m_iSize; - } - - // Simplified versions with ++ and --; avoid using division instruction - int shiftFwd(int basepos) const - { - if (++basepos == m_iSize) - return 0; - return basepos; - } - - int shiftBack(int basepos) const - { - if (basepos == 0) - return m_iSize-1; - return --basepos; - } + bool scanMsg(int& w_start, int& w_end, bool& w_passack); + + int shift(int basepos, int shift) const { return (basepos + shift) % m_iSize; } + + /// Simplified versions with ++ and --; avoid using division instruction + int shiftFwd(int basepos) const + { + if (++basepos == m_iSize) + return 0; + return basepos; + } + + int shiftBack(int basepos) const + { + if (basepos == 0) + return m_iSize - 1; + return --basepos; + } private: - CUnit** m_pUnit; // Array of pointed units collected in the buffer - const int m_iSize; // Size of the internal array of CUnit* items - CUnitQueue* m_pUnitQueue; // the shared unit queue - - int m_iStartPos; // HEAD: first packet available for reading - int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable - // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 - int m_iMaxPos; // delta between acked-TAIL and reception-TAIL - - - int m_iNotch; // the starting read point of the first unit - // (this is required for stream reading mode; it's - // the position in the first unit in the list - // up to which data are already retrieved; - // in message reading mode it's unused and always 0) - - srt::sync::Mutex m_BytesCountLock; // used to protect counters operations - int m_iBytesCount; // Number of payload bytes in the buffer - int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer - int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - - bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode - duration m_tdTsbPdDelay; // aggreed delay - time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode - // Note: m_tsTsbPdTimeBase cumulates values from: - // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: - // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception - // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected - // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). - // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively - // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE - // once the value of average drift exceeds this value in whatever direction. - // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE - // - // XXX Application-supplied timestamps won't work therefore. This requires separate - // calculation of all these things above. - - bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around - static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec) - - /// Max drift (usec) above which TsbPD Time Offset is adjusted - static const int TSBPD_DRIFT_MAX_VALUE = 5000; - /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation - static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; - DriftTracer m_DriftTracer; - AvgBufSize m_mavg; + CUnit** m_pUnit; // Array of pointed units collected in the buffer + const int m_iSize; // Size of the internal array of CUnit* items + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartPos; // HEAD: first packet available for reading + int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable + // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 + int m_iMaxPos; // delta between acked-TAIL and reception-TAIL + + int m_iNotch; // the starting read point of the first unit + // (this is required for stream reading mode; it's + // the position in the first unit in the list + // up to which data are already retrieved; + // in message reading mode it's unused and always 0) + + srt::sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer + int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation + + bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode + duration m_tdTsbPdDelay; // aggreed delay + time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode + // Note: m_tsTsbPdTimeBase cumulates values from: + // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: + // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception + // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected + // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). + // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively + // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE + // once the value of average drift exceeds this value in whatever direction. + // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE + // + // XXX Application-supplied timestamps won't work therefore. This requires separate + // calculation of all these things above. + + bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around + static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) + + /// Max drift (usec) above which TsbPD Time Offset is adjusted + static const int TSBPD_DRIFT_MAX_VALUE = 5000; + /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation + static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; + DriftTracer m_DriftTracer; + AvgBufSize m_mavg; #ifdef SRT_DEBUG_TSBPD_DRIFT - int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) - int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) - int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram - static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram -#endif /* SRT_DEBUG_TSBPD_DRIFT */ + int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) + int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) + int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram + static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram +#endif /* SRT_DEBUG_TSBPD_DRIFT */ #ifdef SRT_DEBUG_TSBPD_OUTJITTER - unsigned long m_ulPdHisto[4][10]; + unsigned long m_ulPdHisto[4][10]; #endif /* SRT_DEBUG_TSBPD_OUTJITTER */ private: - CRcvBuffer(); - CRcvBuffer(const CRcvBuffer&); - CRcvBuffer& operator=(const CRcvBuffer&); + CRcvBuffer(); + CRcvBuffer(const CRcvBuffer&); + CRcvBuffer& operator=(const CRcvBuffer&); }; - #endif From 81d3b95d3f46e9961345c6567210ef1df4659454 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Thu, 8 Apr 2021 10:10:53 -0500 Subject: [PATCH 117/790] [core] Build fix: added ParseFilterConfig declaration (#1918) --- srtcore/packetfilter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index dac988102..545e38e02 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -210,4 +210,6 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer); inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); } inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); } +bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); + #endif From 4849c3c94a75b759f785b89c5a9481ec7a5603ef Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 7 Apr 2021 11:09:54 +0200 Subject: [PATCH 118/790] [core] SRTO_FC: reject values lower than 32 --- srtcore/socketconfig.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 2b0c3943e..29105808b 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -414,11 +414,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - int fc = cast_optval(optval, optlen); - if (fc < 1) + using namespace srt_logging; + const int fc = cast_optval(optval, optlen); + if (fc < co.DEF_MIN_FLIGHT_PKT) + { + LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); throw CUDTException(MJ_NOTSUP, MN_INVAL); + } - co.iFlightFlagSize = std::max(fc, +co.DEF_MIN_FLIGHT_PKT); + co.iFlightFlagSize = fc; } }; From b16b68a995d87bb57a6145776c84f113d2c19ee1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Apr 2021 11:19:10 +0200 Subject: [PATCH 119/790] [docs] Fix SRTO_MINVERSION: readable, default 1.0.0 --- docs/API/API-socket-options.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index a77b97cc2..14d30511e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -219,7 +219,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | | [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | @@ -784,12 +784,14 @@ SCTP protocol. | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | The minimum SRT version that is required from the peer. A connection to a peer that does not satisfy the minimum version requirement will be rejected. See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. +The default value is 0x010000 (SRT v1.0.0). + [Return to list](#list-of-options) --- From 8bf8c5538d0e10fa4f14638388e95ef8a718dcff Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Apr 2021 11:22:28 +0200 Subject: [PATCH 120/790] [docs] Improved SRTO_FC description Co-authored-by: stevomatthews Co-authored-by: cmollahan <43041498+cmollahan@users.noreply.github.com> --- docs/API/API-socket-options.md | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 14d30511e..e8e391ee9 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -394,8 +394,34 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | | `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -Flight Flag Size (maximum number of packets that can be sent without -being acknowledged) +Flow Control limits the maximum number of packets "in flight" - payload (data) packets that were sent +but reception is not yet acknowledged with an ACK control packet. +It also includes data packets already received, but that can't be acknowledged due to loss of preceding data packet(s). +In other words, if a data packet with sequence number `A` was lost, then acknowledgement of the following `SRTO_FC` packets +is blocked until packet `A` is either successfully retransmitted or dropped by the +[Too-Late Packet Drop mechanism](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.6). +Thus the sender will have `SRTO_FC` packets in flight, and will not be allowed to send further data packets. +Therefore, when establishing the value of `SRTO_FC`, it is recommend taking into consideration possible delays due to packet loss and retransmission. + +There is a restriction that the receiver buffer size ([SRTO_RCVBUF](#SRTO_RCVBUF)) must not be greater than `SRTO_FC` +([#700](https://github.com/Haivision/srt/issues/700)). +Therefore, it is recommended to set the value of `SRTO_FC` first, and then the value of `SRTO_RCVBUF`. + +The default flow control window size is 25600 packets. It is approximately: +- 270 Mbits of payload in the default live streaming configuration with an SRT payload size of 1316 bytes; +- 300 Mbits of payload with an SRT payload size of 1456 bytes. + +The minimum number of packets in flight should be (assuming max payload size): +`FCmin = bps / 8 Ɨ RTTsec / (MSS - 44)`, +where +- `bps` - is the payload bitrate of the stream in bits per second; +- `RTTsec` - RTT of the network connection in seconds; +- `MSS` - Maximum segment size (aka MTU), see [SRTO_MSS](#SRTO_MSS); +- 44 - size of headers (20 bytes IPv4 + 8 bytes of UDP + 16 bytes of SRT packet header). + +To avoid blocking the sending of further packets in case of packet loss, the recommended flow control window is +`FC = bps / 8 Ɨ (RTTsec + latency_sec) / (MSS - 44)`, +where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCVLATENCY)) **in seconds**. [Return to list](#list-of-options) From bd55e2945b8f8943418fb496a30953f9796527ac Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 09:51:59 +0200 Subject: [PATCH 121/790] [core] Refact: moved code to processCtrlAckAck dedicated function --- srtcore/core.cpp | 146 +++++++++++++++++++++++------------------------ srtcore/core.h | 18 +++++- 2 files changed, 88 insertions(+), 76 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fc204816d..89cf09b16 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8034,6 +8034,77 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point leaveCS(m_StatsLock); } +void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) +{ + int32_t ack = 0; + + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, tsArrival); + + if (rtt == -1) + { + if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) + { + LOGC(inlog.Warn, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + return; + } + + LOGC(inlog.Error, + log << CONID() << "IPE: ACK record not found, can't estimate RTT " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + return; + } + + if (rtt <= 0) + { + LOGC(inlog.Error, + log << CONID() << "IPE: invalid RTT estimate " << rtt + << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); + return; + } + + // If increasing delay is detected + // sendCtrl(UMSG_CGWARNING); + + // Calculate RTT (EWMA) on the receiver side + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + + updateCC(TEV_ACKACK, EventVariant(ack)); + + // This function will put a lock on m_RecvLock by itself, as needed. + // It must be done inside because this function reads the current time + // and if waiting for the lock has caused a delay, the time will be + // inaccurate. Additionally it won't lock if TSBPD mode is off, and + // won't update anything. Note that if you set TSBPD mode and use + // srt_recvfile (which doesn't make any sense), you'll have a deadlock. + if (m_config.bDriftTracer) + { + steady_clock::duration udrift(0); + steady_clock::time_point newtimebase; + const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, + (udrift), (newtimebase)); +#if ENABLE_EXPERIMENTAL_BONDING + if (drift_updated && m_parent->m_GroupOf) + { + ScopedLock glock(s_UDTUnited.m_GlobControlLock); + if (m_parent->m_GroupOf) + { + m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); + } + } +#endif + } + + // Update last ACK that has been received by the sender + if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) + m_iRcvLastAckAck = ack; +} + void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) { const int32_t* losslist = (int32_t*)(ctrlpkt.m_pcData); @@ -8184,7 +8255,6 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) m_iEXPCount = 1; const steady_clock::time_point currtime = steady_clock::now(); m_tsLastRspTime = currtime; - bool using_rexmit_flag = m_bPeerRexmitFlag; HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" @@ -8197,77 +8267,8 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement - { - int32_t ack = 0; - - // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair - const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, currtime); - - if (rtt == -1) - { - if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) - { - LOGC(inlog.Warn, - log << CONID() << "ACKACK out of order, skipping RTT calculation " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); - break; - } - - LOGC(inlog.Error, - log << CONID() << "IPE: ACK record not found, can't estimate RTT " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); - break; - } - - if (rtt <= 0) - { - LOGC(inlog.Error, - log << CONID() << "IPE: invalid RTT estimate " << rtt - << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); - break; - } - - // If increasing delay is detected - // sendCtrl(UMSG_CGWARNING); - - // Calculate RTT (EWMA) on the receiver side - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); - - updateCC(TEV_ACKACK, EventVariant(ack)); - - // This function will put a lock on m_RecvLock by itself, as needed. - // It must be done inside because this function reads the current time - // and if waiting for the lock has caused a delay, the time will be - // inaccurate. Additionally it won't lock if TSBPD mode is off, and - // won't update anything. Note that if you set TSBPD mode and use - // srt_recvfile (which doesn't make any sense), you'll have a deadlock. - if (m_config.bDriftTracer) - { - steady_clock::duration udrift(0); - steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, - (udrift), (newtimebase)); -#if ENABLE_EXPERIMENTAL_BONDING - if (drift_updated && m_parent->m_GroupOf) - { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_GroupOf) - { - m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); - } - } -#endif - } - - // Update last ACK that has been received by the sender - if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) - m_iRcvLastAckAck = ack; - + processCtrlAckAck(ctrlpkt, currtime); break; - } case UMSG_LOSSREPORT: // 011 - Loss Report processCtrlLossReport(ctrlpkt); @@ -8284,9 +8285,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_KEEPALIVE: // 001 - Keep-alive - handleKeepalive(ctrlpkt.m_pcData, ctrlpkt.getLength()); - break; case UMSG_HANDSHAKE: // 000 - Handshake @@ -8416,6 +8415,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_DROPREQ: // 111 - Msg drop request { + const bool using_rexmit_flag = m_bPeerRexmitFlag; UniqueLock rlock(m_RecvLock); m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); // When the drop request was received, it means that there are diff --git a/srtcore/core.h b/srtcore/core.h index 9ee3df6d7..d9f966d94 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -943,13 +943,25 @@ class CUDT /// /// @returns the nmber of packets sent. int sendCtrlAck(CPacket& ctrlpkt, int size); + void sendLossReport(const std::vector< std::pair >& losslist); void processCtrl(const CPacket& ctrlpkt); - void sendLossReport(const std::vector< std::pair >& losslist); - void processCtrlAck(const CPacket& ctrlpkt, const time_point &currtime); + + /// @brief Process incoming control ACK packet. + /// @param ctrlpkt incoming packet + /// @param currtime current clock time + void processCtrlAck(const CPacket& ctrlpkt, const time_point& currtime); + + /// @brief Process incoming control ACKACK packet. + /// @param ctrlpkt incoming packet + /// @param tsArrival time when packet has arrived (used to calculate RTT) + void processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival); + + /// @brief Process incoming loss report (NAK) packet. + /// @param ctrlpkt incoming NAK packet void processCtrlLossReport(const CPacket& ctrlpkt); - /// + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From 7a2b3a22418e050cb06a998f92ffe59162a71d02 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 09:57:00 +0200 Subject: [PATCH 122/790] [core] Refact: moved code to processCtrlHS dedicated function --- srtcore/core.cpp | 222 ++++++++++++++++++++++++----------------------- srtcore/core.h | 8 +- 2 files changed, 118 insertions(+), 112 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 89cf09b16..25fc8abb8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8249,6 +8249,117 @@ void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) leaveCS(m_StatsLock); } +void CUDT::processCtrlHS(const CPacket& ctrlpkt) +{ + CHandShake req; + req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); + + HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); + + if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? + || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION + { + // The peer side has not received the handshake message, so it keeps querying + // resend the handshake packet + + // This condition embraces cases when: + // - this is normal accept() and URQ_INDUCTION was received + // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS + // or CONCLUSION) + // - this is any of URQ_ERROR_* - well... + CHandShake initdata; + initdata.m_iISN = m_iISN; + initdata.m_iMSS = m_config.iMSS; + initdata.m_iFlightFlagSize = m_config.iFlightFlagSize; + + // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. + // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. + initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; + initdata.m_iID = m_SocketID; + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + bool have_hsreq = false; + if (req.m_iVersion > HS_VERSION_UDT4) + { + initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener... + const int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (hs_flags != 0) // has SRT extensions + { + HLOGC(inlog.Debug, + log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) + << " WITH SRT ext"); + have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); + if (!have_hsreq) + { + initdata.m_iVersion = 0; + m_RejectReason = SRT_REJ_ROGUE; + initdata.m_iReqType = URQFailure(m_RejectReason); + } + else + { + // Extensions are added only in case of CONCLUSION (not AGREEMENT). + // Actually what is expected here is that this may either process the + // belated-repeated handshake from a caller (and then it's CONCLUSION, + // and should be added with HSRSP/KMRSP), or it's a belated handshake + // of Rendezvous when it has already considered itself connected. + // Sanity check - according to the rules, there should be no such situation + if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER) + { + LOGC(inlog.Error, + log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " + "handshake phase."); + } + + // The 'extension' flag will be set from this variable; set it to false + // in case when the AGREEMENT response is to be sent. + have_hsreq = initdata.m_iReqType == URQ_CONCLUSION; + HLOGC(inlog.Debug, + log << CONID() << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType) + << " kmdatasize=" << kmdatasize); + } + } + else + { + HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)); + } + } + else + { + initdata.m_iVersion = HS_VERSION_UDT4; + kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX + } + + initdata.m_extension = have_hsreq; + + HLOGC(inlog.Debug, + 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); + + // 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. + if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, + (response), (initdata))) + { + response.m_iID = m_PeerID; + setPacketTS(response, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + if (nbsent) + { + m_tsLastSndTime = steady_clock::now(); + } + } + } + else + { + HLOGC(inlog.Debug, log << CONID() << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED."); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8289,117 +8400,8 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_HANDSHAKE: // 000 - Handshake - { - CHandShake req; - req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); - - HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); - - if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? - || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION - { - // The peer side has not received the handshake message, so it keeps querying - // resend the handshake packet - - // This condition embraces cases when: - // - this is normal accept() and URQ_INDUCTION was received - // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS - // or CONCLUSION) - // - this is any of URQ_ERROR_* - well... - CHandShake initdata; - initdata.m_iISN = m_iISN; - initdata.m_iMSS = m_config.iMSS; - initdata.m_iFlightFlagSize = m_config.iFlightFlagSize; - - // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. - // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. - initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; - initdata.m_iID = m_SocketID; - - uint32_t kmdata[SRTDATA_MAXSIZE]; - size_t kmdatasize = SRTDATA_MAXSIZE; - bool have_hsreq = false; - if (req.m_iVersion > HS_VERSION_UDT4) - { - initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener... - int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); - if (hs_flags != 0) // has SRT extensions - { - HLOGC(inlog.Debug, - log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) - << " WITH SRT ext"); - have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); - if (!have_hsreq) - { - initdata.m_iVersion = 0; - m_RejectReason = SRT_REJ_ROGUE; - initdata.m_iReqType = URQFailure(m_RejectReason); - } - else - { - // Extensions are added only in case of CONCLUSION (not AGREEMENT). - // Actually what is expected here is that this may either process the - // belated-repeated handshake from a caller (and then it's CONCLUSION, - // and should be added with HSRSP/KMRSP), or it's a belated handshake - // of Rendezvous when it has already considered itself connected. - // Sanity check - according to the rules, there should be no such situation - if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER) - { - LOGC(inlog.Error, - log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " - "handshake phase."); - } - - // The 'extension' flag will be set from this variable; set it to false - // in case when the AGREEMENT response is to be sent. - have_hsreq = initdata.m_iReqType == URQ_CONCLUSION; - HLOGC(inlog.Debug, - log << CONID() << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType) - << " kmdatasize=" << kmdatasize); - } - } - else - { - HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)); - } - } - else - { - initdata.m_iVersion = HS_VERSION_UDT4; - kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX - } - - initdata.m_extension = have_hsreq; - - HLOGC(inlog.Debug, - 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); - - // 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. - if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) - { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); - if (nbsent) - { - m_tsLastSndTime = steady_clock::now(); - } - } - } - else - { - HLOGC(inlog.Debug, log << CONID() << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED."); - } - + processCtrlHS(ctrlpkt); break; - } case UMSG_SHUTDOWN: // 101 - Shutdown m_bShutdown = true; diff --git a/srtcore/core.h b/srtcore/core.h index d9f966d94..7da1c8f36 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -948,12 +948,12 @@ class CUDT void processCtrl(const CPacket& ctrlpkt); /// @brief Process incoming control ACK packet. - /// @param ctrlpkt incoming packet + /// @param ctrlpkt incoming ACK packet /// @param currtime current clock time void processCtrlAck(const CPacket& ctrlpkt, const time_point& currtime); /// @brief Process incoming control ACKACK packet. - /// @param ctrlpkt incoming packet + /// @param ctrlpkt incoming ACKACK packet /// @param tsArrival time when packet has arrived (used to calculate RTT) void processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival); @@ -961,6 +961,10 @@ class CUDT /// @param ctrlpkt incoming NAK packet void processCtrlLossReport(const CPacket& ctrlpkt); + /// @brief Process incoming handshake control packet + /// @param ctrlpkt incoming HS packet + void processCtrlHS(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From eb6e0296bc019d82754c33262f763bd4696ec6eb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:01:14 +0200 Subject: [PATCH 123/790] [core] Refact: moved code to processCtrlDropReq dedicated function --- srtcore/core.cpp | 78 +++++++++++++++++++++++++----------------------- srtcore/core.h | 4 +++ 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 25fc8abb8..1c7019248 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8360,6 +8360,45 @@ void CUDT::processCtrlHS(const CPacket& ctrlpkt) } } +void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) +{ + { + const bool using_rexmit_flag = m_bPeerRexmitFlag; + UniqueLock rlock(m_RecvLock); + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); + // When the drop request was received, it means that there are + // packets for which there will never be ACK sent; if the TSBPD thread + // is currently in the ACK-waiting state, it may never exit. + if (m_bTsbPd) + { + HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); + CSync cc(m_RcvTsbPdCond, rlock); + cc.signal_locked(rlock); + } + } + + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; + + dropFromLossLists(dropdata[0], dropdata[1]); + + // move forward with current recv seq no. + // SYMBOLIC: + // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo + // && dropdata[1] >% m_iRcvCurrSeqNo ) + if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) + && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) + { + HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" + << dropdata[0] << "-" << dropdata[1] << " <-- set as current seq"); + m_iRcvCurrSeqNo = dropdata[1]; + } + else + { + HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" + << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8416,44 +8455,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_DROPREQ: // 111 - Msg drop request - { - const bool using_rexmit_flag = m_bPeerRexmitFlag; - UniqueLock rlock(m_RecvLock); - m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - CSync cc(m_RcvTsbPdCond, rlock); - cc.signal_locked(rlock); - } - } - - { - int32_t* dropdata = (int32_t*)ctrlpkt.m_pcData; - - dropFromLossLists(dropdata[0], dropdata[1]); - - // move forward with current recv seq no. - // SYMBOLIC: - // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo - // && dropdata[1] >% m_iRcvCurrSeqNo ) - if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) - && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) - { - HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " <-- set as current seq"); - m_iRcvCurrSeqNo = dropdata[1]; - } - else - { - HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); - } - } - + processCtrlDropReq(ctrlpkt); break; case UMSG_PEERERROR: // 1000 - An error has happened to the peer side diff --git a/srtcore/core.h b/srtcore/core.h index 7da1c8f36..ae847032a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -965,6 +965,10 @@ class CUDT /// @param ctrlpkt incoming HS packet void processCtrlHS(const CPacket& ctrlpkt); + /// @brief Process incoming drop request control packet + /// @param ctrlpkt incoming drop request packet + void processCtrlDropReq(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From efd0c0b24ce7ae3e2ceceec3c371a71a3a35f365 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:03:19 +0200 Subject: [PATCH 124/790] [core] Refact: moved code to processCtrlShutdown dedicated function --- srtcore/core.cpp | 23 ++++++++++++++--------- srtcore/core.h | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1c7019248..98b9c4deb 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8399,6 +8399,19 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } +void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) +{ + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + // This does the same as it would happen on connection timeout, + // just we know about this state prematurely thanks to this message. + updateBrokenConnection(); + completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8443,15 +8456,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - m_bShutdown = true; - m_bClosing = true; - m_bBroken = true; - m_iBrokenCounter = 60; - - // This does the same as it would happen on connection timeout, - // just we know about this state prematurely thanks to this message. - updateBrokenConnection(); - completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! + processCtrlShutdown(ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request diff --git a/srtcore/core.h b/srtcore/core.h index ae847032a..f0bec9dce 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -969,6 +969,10 @@ class CUDT /// @param ctrlpkt incoming drop request packet void processCtrlDropReq(const CPacket& ctrlpkt); + /// @brief Process incoming shutdown control packet + /// @param ctrlpkt incoming shutdown packet + void processCtrlShutdown(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From d5f1d08132136224bff66b9b68024df350a36547 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:05:42 +0200 Subject: [PATCH 125/790] [core] Refact: moved code to processCtrlUserDefined dedicated function --- srtcore/core.cpp | 59 +++++++++++++++++++++++++----------------------- srtcore/core.h | 4 ++++ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 98b9c4deb..381669b30 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8412,6 +8412,36 @@ void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! } +void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) +{ + HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) + << ", value=" << ctrlpkt.getExtendedType()); + + // This has currently two roles in SRT: + // - HSv4 (legacy) handshake + // - refreshed KMX (initial KMX is done still in the HS process in HSv5) + const bool understood = processSrtMsg(&ctrlpkt); + // CAREFUL HERE! This only means that this update comes from the UMSG_EXT + // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean + // the handshake version, but the reason of calling this function. + // + // Fortunately, the only messages taken into account in this function + // are HSREQ and HSRSP, which should *never* be interchanged when both + // parties are HSv5. + if (understood) + { + if (ctrlpkt.getExtendedType() == SRT_CMD_HSREQ || ctrlpkt.getExtendedType() == SRT_CMD_HSRSP) + { + updateAfterSrtHandshake(HS_VERSION_UDT4); + } + } + else + { + updateCC(TEV_CUSTOM, EventVariant(&ctrlpkt)); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8469,39 +8499,12 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // currently only this error is signalled from the peer side // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately // giving the app a chance to fix the issue - m_bPeerHealth = false; break; case UMSG_EXT: // 0x7FFF - reserved and user defined messages - HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) - << ", value=" << ctrlpkt.getExtendedType()); - { - // This has currently two roles in SRT: - // - HSv4 (legacy) handshake - // - refreshed KMX (initial KMX is done still in the HS process in HSv5) - bool understood = processSrtMsg(&ctrlpkt); - // CAREFUL HERE! This only means that this update comes from the UMSG_EXT - // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean - // the handshake version, but the reason of calling this function. - // - // Fortunately, the only messages taken into account in this function - // are HSREQ and HSRSP, which should *never* be interchanged when both - // parties are HSv5. - if (understood) - { - if (ctrlpkt.getExtendedType() == SRT_CMD_HSREQ || ctrlpkt.getExtendedType() == SRT_CMD_HSRSP) - { - updateAfterSrtHandshake(HS_VERSION_UDT4); - } - } - else - { - updateCC(TEV_CUSTOM, EventVariant(&ctrlpkt)); - } - } + processCtrlUserDefined(ctrlpkt); break; default: diff --git a/srtcore/core.h b/srtcore/core.h index f0bec9dce..6be1a9011 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -973,6 +973,10 @@ class CUDT /// @param ctrlpkt incoming shutdown packet void processCtrlShutdown(const CPacket& ctrlpkt); + /// @brief Process incoming user defined control packet + /// @param ctrlpkt incoming user defined packet + void processCtrlUserDefined(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From f1f2dd4d7699d978ec7815e84713d2beb51595ad Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:04:02 +0200 Subject: [PATCH 126/790] [docs] Reorganized and cleaned up the docs folder (#1911) --- CONTRIBUTING.md | 2 +- README.md | 12 +-- docs/API/API-functions.md | 6 +- docs/API/API-socket-options.md | 8 +- docs/API/API.md | 6 +- docs/API/statistics.md | 22 +++--- docs/README.md | 69 ++++++++++++++++-- docs/{ => apps}/srt-live-transmit.md | 0 .../srt-multiplex.md} | 0 docs/{ => apps}/srt-tunnel.md | 0 .../Compiling.md => build/build-android.md} | 26 +++++-- docs/{build_iOS.md => build/build-iOS.md} | 0 .../build-options.md} | 6 +- docs/{ => build}/build-win.md | 2 +- .../developers-guide.md} | 12 +-- .../low-level-info.md} | 0 .../making-srt-better.md} | 0 .../access-control.md} | 6 +- docs/{ => features}/bonding-intro.md | 2 +- docs/{ => features}/encryption.md | 38 ++++++---- docs/{ => features}/handshake.md | 10 +-- .../images/block-aligned-5rx10c.png | Bin docs/{ => features}/images/block-aligned.png | Bin ...n-block-aligned-5rx10c-deleted-packets.png | Bin .../images/non-block-aligned-5rx10c.png | Bin .../images/non-block-aligned.png | Bin .../images/packet-filter-mechanism.png | Bin .../images/rebuild-missing-sequence.png | Bin .../images/srt-encryption-1.png | Bin .../images/srt-encryption-2.png | Bin .../images/staircase-pattern-5rx10c.png | Bin docs/{ => features}/live-streaming.md | 17 ++--- .../packet-filtering-and-fec.md | 16 ++-- docs/{ => features}/socket-groups.md | 0 docs/gstreamer.md | 62 ---------------- .../images/srt-history-good-signal.png} | Bin .../images/srt-transmission-bad-signal.png} | Bin docs/{ => misc}/why-srt-was-created.md | 4 +- scripts/build-android/README.md | 5 ++ {docs/Android => scripts/build-android}/mkall | 0 {docs/Android => scripts/build-android}/mksrt | 0 {docs/Android => scripts/build-android}/mkssl | 0 .../Android => scripts/build-android}/packjni | 0 .../build-android}/prepare_build | 0 srtcore/api.h | 2 +- 45 files changed, 174 insertions(+), 159 deletions(-) rename docs/{ => apps}/srt-live-transmit.md (100%) rename docs/{SRT-Multiplex.md => apps/srt-multiplex.md} (100%) rename docs/{ => apps}/srt-tunnel.md (100%) rename docs/{Android/Compiling.md => build/build-android.md} (76%) rename docs/{build_iOS.md => build/build-iOS.md} (100%) rename docs/{BuildOptions.md => build/build-options.md} (98%) rename docs/{ => build}/build-win.md (99%) rename docs/{DevelopersGuide.md => dev/developers-guide.md} (94%) rename docs/{LowLevelInfo.md => dev/low-level-info.md} (100%) rename docs/{reporting.md => dev/making-srt-better.md} (100%) rename docs/{AccessControl.md => features/access-control.md} (99%) rename docs/{ => features}/bonding-intro.md (99%) rename docs/{ => features}/encryption.md (95%) rename docs/{ => features}/handshake.md (99%) rename docs/{ => features}/images/block-aligned-5rx10c.png (100%) rename docs/{ => features}/images/block-aligned.png (100%) rename docs/{ => features}/images/non-block-aligned-5rx10c-deleted-packets.png (100%) rename docs/{ => features}/images/non-block-aligned-5rx10c.png (100%) rename docs/{ => features}/images/non-block-aligned.png (100%) rename docs/{ => features}/images/packet-filter-mechanism.png (100%) rename docs/{ => features}/images/rebuild-missing-sequence.png (100%) rename docs/{ => features}/images/srt-encryption-1.png (100%) rename docs/{ => features}/images/srt-encryption-2.png (100%) rename docs/{ => features}/images/staircase-pattern-5rx10c.png (100%) rename docs/{ => features}/live-streaming.md (95%) rename docs/{ => features}/packet-filtering-and-fec.md (98%) rename docs/{ => features}/socket-groups.md (100%) delete mode 100644 docs/gstreamer.md rename docs/{images/SRT_History_Good_Signal.png => misc/images/srt-history-good-signal.png} (100%) rename docs/{images/SRT_Transmission_Bad_Signal.png => misc/images/srt-transmission-bad-signal.png} (100%) rename docs/{ => misc}/why-srt-was-created.md (97%) create mode 100644 scripts/build-android/README.md rename {docs/Android => scripts/build-android}/mkall (100%) rename {docs/Android => scripts/build-android}/mksrt (100%) rename {docs/Android => scripts/build-android}/mkssl (100%) rename {docs/Android => scripts/build-android}/packjni (100%) rename {docs/Android => scripts/build-android}/prepare_build (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6f226602..239e25a2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Submit a pull request at any time, whether an issue has been created or not. It * SRT protocol definitions * portability and platform-specific parts -Please follow the [Developer's guide](./docs/DevelopersGuide.md). +Please follow the [SRT Developer's Guide](docs/dev/developers-guide.md). ## Code Style diff --git a/README.md b/README.md index eda49484e..1c9aff2d8 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,14 @@ As audio/video packets are streamed from a source to a destination device, SRT d ### Guides * [SRT API Documents](docs/API/) -* [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) -* [Developer's Guide](docs/DevelopersGuide.md) +* [Using the `srt-live-transmit` App](docs/apps/srt-live-transmit.md) +* [SRT Developer's Guide](docs/dev/developers-guide.md) * [Contributing](CONTRIBUTING.md) -* [Reporting problems](docs/reporting.md) +* [Reporting Issues](docs/dev/making-srt-better.md) * SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) * SRT CookBook: [Website](https://srtlab.github.io/srt-cookbook), [GitHub Repo](https://github.com/SRTLab/srt-cookbook) * [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) -* [Why SRT Was Created](docs/why-srt-was-created.md) +* [Why SRT Was Created](docs/misc/why-srt-was-created.md) ## Requirements @@ -50,7 +50,7 @@ As audio/video packets are streamed from a source to a destination device, SRT d * OpenSSL * Pthreads (for POSIX systems it's builtin, for Windows there's a library) -For detailed description of the build system and options, please read [BuildOptions.md](docs/BuildOptions.md). +For detailed description of the build system and options, please read [SRT Build Options](docs/build/build-options.md). ### Build on Linux @@ -127,7 +127,7 @@ make ### Build on Windows -Follow the [Windows build instructions](docs/build-win.md). +Follow the [Building SRT for Windows](docs/build/build-win.md) instructions. [appveyor-badge]: https://img.shields.io/appveyor/ci/Haivision/srt/master.svg?label=Windows [appveyor]: https://ci.appveyor.com/project/Haivision/srt diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 734ce6221..0e65d5ab3 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1507,7 +1507,7 @@ The following options are allowed to be set on the member socket: * [srt_setsockopt, srt_setsockflag](#srt_setsockopt-srt_setsockflag) * [srt_getversion](#srt_getversion) -**NOTE**: For more information, see [Getting and Setting Options](API-socket-options.md#getting-and-setting-options). +**NOTE**: For more information, see [SRT API Socket Options, Getting and Setting Options](API-socket-options.md#getting-and-setting-options). ### srt_getpeername ``` @@ -1732,8 +1732,8 @@ call [`srt_sendmsg2`](#srt_sendmsg) or [`srt_recvmsg2`](#srt_recvmsg2) function for a group, you should pass an array here so that you can retrieve the status of particular member sockets. If you pass an array that is too small, your `grpdata_size` field will be rewritten with the current number of members, but without filling in -the array. For details, see the (Bonding introduction)[../bonding-intro.md] and -(Socket Groups)[../socket-groups.md] documents. +the array. For details, see the [SRT Connection Bonding](../features/bonding-intro.md) and +[SRT Socket Groups](../features/socket-groups.md) documents. **Helpers for [`SRT_MSGCTRL`](#SRT_MSGCTRL):** diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index e8e391ee9..2680e060f 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -796,7 +796,7 @@ complete (not all packets received or there was a packet loss) it will not be copied to the application's buffer. Messages that are sent later, but were earlier reassembled by the receiver, will be delivered once ready, if the `inorder` flag was set to false. -See [`srt_sendmsg`](API.md#sending-and-receiving)). +See [`srt_sendmsg`](API.md#sending-and-receiving). As a comparison to the standard system protocols, the Stream API does transmission similar to TCP, whereas the Message API functions like the @@ -920,7 +920,7 @@ The connection will be rejected with `SRT_REJ_FILTER` code in the following case In case of the built-in `fec` filter, the mandatory parameter is `cols`, all others have their default values. For example, the configuration specified as `fec,cols:10` is `fec,cols:10,rows:1,arq:onreq,layout:even`. See how to -[configure the FEC Filter](packet-filtering-and-fec.md#configuring-the-fec-filter). +configure the FEC filter in [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md#configuring-the-fec-filter). Below in the table are examples for the built-in `fec` filter. Note that the negotiated config need not have parameters in the given order. @@ -950,7 +950,7 @@ Reading this option after the connection is established will return the full configuration that has been agreed upon by both parties (including default values). -For details, see [Packet Filtering & FEC](packet-filtering-and-fec.md). +For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). [Return to list](#list-of-options) @@ -1455,7 +1455,7 @@ will be able to retrieve this stream ID from the socket that is returned from `srt_accept` (for a connected socket with that stream ID). You usually use SET on the socket used for `srt_connect`, and GET on the socket retrieved from `srt_accept`. This string can be used completely free-form. However, it's highly -recommended to follow the [SRT Access Control guidlines](../AccessControl.md). +recommended to follow the [SRT Access Control (Stream ID) Guidlines](../features/access-control.md). - As this uses internally the `std::string` type, there are additional functions for it in the legacy/C++ API (udt.h): `srt::setstreamid` and `srt::getstreamid`. diff --git a/docs/API/API.md b/docs/API/API.md index f1d3a1209..594f9a12d 100644 --- a/docs/API/API.md +++ b/docs/API/API.md @@ -281,7 +281,7 @@ nb = srt_recvmsg2(u, buf, nb, &mc); ### Transmission Types Available in SRT Mode settings determine how the sender and receiver functions work. The main -[socket options](API-socket-options.md) that control it are: +SRT [socket options](API-socket-options.md) that control it are: - `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected mode: @@ -519,7 +519,7 @@ either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. ### Transmission Method: Live -Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](API-socket-options.md): +Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [socket options](API-socket-options.md): - `SRTO_TSBPDMODE` = true - `SRTO_RCVLATENCY` = 120 @@ -596,7 +596,7 @@ NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. ### Transmission Method: Buffer -Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](API-socket-options.md): +Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [socket options](API-socket-options.md): - `SRTO_TSBPDMODE` = false - `SRTO_RCVLATENCY` = 0 diff --git a/docs/API/statistics.md b/docs/API/statistics.md index f82b8074c..156224fd0 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -246,7 +246,7 @@ The total number of packets that failed to be decrypted at the receiver side. Av #### pktSndFilterExtraTotal -The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for sender. +The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for sender. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. @@ -254,7 +254,7 @@ If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket O #### pktRcvFilterExtraTotal -The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. @@ -262,13 +262,13 @@ If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket O #### pktRcvFilterSupplyTotal -The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterLossTotal -The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. @@ -363,25 +363,25 @@ Same as [pktRecvNAKTotal](#pktRecvNAKTotal), but for a specified interval. Same as [pktSndFilterExtraTotal](#pktSndFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterExtra Same as [pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterSupply Same as [pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterLoss Same as [pktRcvFilterLossTotal](#pktRcvFilterLossTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### mbpsSendRate @@ -727,12 +727,12 @@ that is received late. SRT group statistics are implemented for SRT Connection Bonding feature and available since SRT v1.5.0. Check the following documentation and code examples for details: -- [Introduction in SRT Connection Bonding feature](../bonding-intro.md), -- [The concept of socket groups](../socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, +- Introduction in [SRT Connection Bonding](../features/bonding-intro.md), +- The concept of [SRT Socket Groups](../features/socket-groups.md). Here you will also find the information regarding the `srt-test-live` application for testing Connection Bonding, - Check also [SRT API](API.md) and [SRT API Functions](API-functions.md) documentation for Connection Bonding related updates, - Code examples: simple [client](https://github.com/Haivision/srt/blob/master/examples/test-c-client-bonding.c) and [server](https://github.com/Haivision/srt/blob/master/examples/test-c-server-bonding.c) implementation. -`srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [API functions](API-functions.md) for usage instructions. +`srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [SRT API Functions](API-functions.md) for usage instructions. ### Summary Table diff --git a/docs/README.md b/docs/README.md index ed2ad475c..2d91117d7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,67 @@ # Documentation Overview + + ## SRT API Documents -| Folder Name | File Name | Description | Refer as | -| :---------: | ------------------------------------------------------ | --------------------------------------------------- | ---------------------- | -| API | [API.md](API/API.md) | Detailed description of the SRT C API | SRT API | -| API | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions | SRT API Functions | -| API | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API | SRT API Socket Options | -| API | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics | SRT Statistics | +| Document Title | Folder | File Name | Description | +| :-------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | +| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | +| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | +| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | +| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | +| | | | | + +## Build Instructions + +| Document Title | Folder | File Name | Description | +| :------------------------------------------------- | :---------------------------- | :----------------------------------------- | :----------------------------------------------------------- | +| [Building SRT for Android](build/build-android.md) | [build](build/) | [build-android.md](build/build-android.md) | SRT build instructions for Android. | +| [Building SRT for iOS](build/build-iOS.md) | [build](build/) | [build-iOS.md](build/build-iOS.md) | SRT build instructions for iOS. | +| [SRT Build Options](build/build-options.md) | [build](build/) | [build-options.md](build/build-options.md) | Description of CMake build system, configure script, and
build options. | +| [Building SRT for Windows](build/build-win.md) | [build](build/) | [build-win.md](build/build-win.md) | SRT build instructions for Windows. | +| | | | | + +## Development Documents + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------- | :---------------------------- | :----------------------------------------------- | :----------------------------------------------------------- | +| [SRT Developer's Guide](dev/developers-guide.md) | [dev](dev/) | [developers-guide.md](dev/developers-guide.md) | Development setup, project structure, coding rules,
submitting issues & PRs, etc. | +| [Low Level Info](dev/low-level-info.md) | [dev](dev/) | [low-level-info.md](dev/low-level-info.md) | Low level information for the SRT project (only
mutex locking). | +| [Making SRT Better](dev/making-srt-better.md) | [dev](dev/) | [making-srt-better.md](dev/making-srt-better.md) | Guidelines for problem reporting, collecting debug logs
and pcaps. | +| | | | | + +## Features + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------------- | :---------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| [SRT Access Control](features/access-control.md)
[(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | +| [SRT Connection Bonding](features/bonding-intro.md) | [features](features/) | [bonding-intro.md](features/bonding-intro.md) | Introduction to Connection Bonding. Description
of group (bonded) connections. | +| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | +| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | +| [Live Streaming](features/live-streaming.md)
[Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| [SRT Packet](features/packet-filtering-and-fec.md)
[Filtering & FEC](features/packet-filtering-and-fec.md) | [features](features/) | [packet-filtering-and-fec.md](features/packet-filtering-and-fec.md) | Description of SRT packet filtering mechanism,
including FEC. | +| [SRT Socket Groups](features/socket-groups.md) | [features](features/) | [socket-groups.md](features/socket-groups.md) | Description of socket groups in SRT (Connection
Bonding). Here you will also find the information
regarding the `srt-test-live` application for testing
Connection Bonding. | +| | | | | + +## Sample Applications + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | +| [Using the](apps/srt-live-transmit.md)
[`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the](apps/srt-multiplex.md)
[`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | +| [Using the](apps/srt-tunnel.md)
[`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| | | | | + +## Miscellaneous + +| Document Title | Folder | File Name | Description | +| :------------------------------------------------- | :---------------------------- | :---------------------------------------------------- | :----------------------------------------------------------- | +| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| | | | | + diff --git a/docs/srt-live-transmit.md b/docs/apps/srt-live-transmit.md similarity index 100% rename from docs/srt-live-transmit.md rename to docs/apps/srt-live-transmit.md diff --git a/docs/SRT-Multiplex.md b/docs/apps/srt-multiplex.md similarity index 100% rename from docs/SRT-Multiplex.md rename to docs/apps/srt-multiplex.md diff --git a/docs/srt-tunnel.md b/docs/apps/srt-tunnel.md similarity index 100% rename from docs/srt-tunnel.md rename to docs/apps/srt-tunnel.md diff --git a/docs/Android/Compiling.md b/docs/build/build-android.md similarity index 76% rename from docs/Android/Compiling.md rename to docs/build/build-android.md index fbfe8893c..f2dec839e 100644 --- a/docs/Android/Compiling.md +++ b/docs/build/build-android.md @@ -1,5 +1,11 @@ -# Establishing a Build Environment -## Installing the Android NDK +# Building SRT for Android + +**NOTE:** The scripts have been moved to [scripts/build-android](../../scripts/build-android/) folder. + +## Establishing a Build Environment + +### Installing the Android NDK + The Android NDK is required to build native modules for Android. Consider installing the latest version of cmake. The higher version of cmake the better. As of writing the current version of CMake is 3.18.4 You can download Cmake from the following website: @@ -8,11 +14,19 @@ You can download Cmake from the following website: Download the NDK r19 or newer archive from the following site: [Download the Android NDK on developer.android.com](https://developer.android.com/ndk/downloads/index.html) To install the Android NDK, simply expand the archive in the folder where you want to install it. -## OpenSSL + +### OpenSSL + Google removed openssl from Android 7+. You must build openssl libs by yourself. -# Configure the NDK path + +## Configure the NDK Path + Edit the ```mkall``` script to configure NDK path. Set the ```NDK``` to the directory where the NDK is installed. -# Build SRT for Android + +## Build SRT for Android + Run ```/bin/bash mkall > build.log``` script. Libraries will be installed to ```./target-architecture/lib```. -# Export SRT libraries + +## Export SRT Libraries + Run ```/bin/bash packjni``` to generate ```jniLibs``` archive for Android Studio. diff --git a/docs/build_iOS.md b/docs/build/build-iOS.md similarity index 100% rename from docs/build_iOS.md rename to docs/build/build-iOS.md diff --git a/docs/BuildOptions.md b/docs/build/build-options.md similarity index 98% rename from docs/BuildOptions.md rename to docs/build/build-options.md index adf718031..805214338 100644 --- a/docs/BuildOptions.md +++ b/docs/build/build-options.md @@ -10,10 +10,10 @@ interpreter) that can make operating with the options easier. The `cmake` build system was tested on the following platforms: - Linux (various flavors) - - macOS (see this [separate document](build_iOS.md)) + - macOS (see [Building SRT for iOS](build-iOS.md)) - Windows with MinGW - - Windows with Microsoft Visual Studio (see this [separate document](build-win.md)) - - Android (see this [separate document](Android/Compiling.md)) + - Windows with Microsoft Visual Studio (see [Building SRT for Windows](build-win.md)) + - Android (see [Building SRT for Android](build-android.md)) - Cygwin (only for testing) The `configure` script wasn't tested on Windows (other than on Cygwin). diff --git a/docs/build-win.md b/docs/build/build-win.md similarity index 99% rename from docs/build-win.md rename to docs/build/build-win.md index f3c3c1b3c..3f47b310b 100644 --- a/docs/build-win.md +++ b/docs/build/build-win.md @@ -1,4 +1,4 @@ -# SRT Build Instructions +# Building SRT for Windows diff --git a/docs/DevelopersGuide.md b/docs/dev/developers-guide.md similarity index 94% rename from docs/DevelopersGuide.md rename to docs/dev/developers-guide.md index cc021cb6c..3084373e1 100644 --- a/docs/DevelopersGuide.md +++ b/docs/dev/developers-guide.md @@ -66,21 +66,21 @@ cmake .. -DENABLE_UNITTESTS=ON cmake --build ./ ``` -**Note.** If you're using Windows, please refer to [Windows Build Instructions](./build-win.md). +**Note.** If you are using Windows, please refer to [Building SRT for Windows](../build/build-win.md) instructions. -**Note.** Please see the following document for the build options: [BuildOptions.md](./BuildOptions.md). +**Note.** Please see the following document for the build options: [SRT Build Options](../build/build-options.md). To see the full list of make options run `cmake .. -LAH` from the `_build` folder. **Note.** There is an alternative `configure` script provided. It is **NOT** an alternative Autotools build, but a convenience script. It processes the usual format of `--long-options` and calls `cmake` with appropriate options in the end. This script is dependent on "tcl" package. -Please see the following document for `configure` usage: [BuildOptions.md](./BuildOptions.md). +Please see the following document for `configure` usage: [SRT Build Options](../build/build-options.md). The build output is in the `_build` directory. The following applications can be found there. * `srt-live-transmit` - A sample application to transmit a live stream from source medium (UDP/SRT/`stdin`) -to the target medium (UDP/SRT/`stdout`). See [srt-live-transmit.md](./srt-live-transmit.md) for more info. +to the target medium (UDP/SRT/`stdout`). See [Using the `srt-live-transmit` App](../apps/srt-live-transmit.md) for more info. * `srt-file-transmit` - A sample application to transmit files with SRT. -* `srt-tunnel` - A sample application to set up an SRT tunnel for TCP traffic. See [srt-tunnel.md](./srt-tunnel.md) for more info. +* `srt-tunnel` - A sample application to set up an SRT tunnel for TCP traffic. See [Using the `srt-tunnel` App](../apps/srt-tunnel.md) for more info. * `tests-srt` - unit testing application. ## Language standard requirements @@ -111,7 +111,7 @@ for example). The SRT installation has the following folders: -* apps - the folder contains [srt-live-transmit](./srt-live-transmit.md), `srt-file-transmit` and `srt-tunnel` sample applications. +* apps - the folder contains [srt-live-transmit](../apps/srt-live-transmit.md), `srt-file-transmit` and [srt-tunnel](../apps/srt-tunnel.md) sample applications. * *common - holds some platform-dependent code. * *docs - contains all the documentation in the GitHub Markdown format. * *examples - example applications (use `-DENABLE_EXAMPLES=ON` CMake build option to include in the build) diff --git a/docs/LowLevelInfo.md b/docs/dev/low-level-info.md similarity index 100% rename from docs/LowLevelInfo.md rename to docs/dev/low-level-info.md diff --git a/docs/reporting.md b/docs/dev/making-srt-better.md similarity index 100% rename from docs/reporting.md rename to docs/dev/making-srt-better.md diff --git a/docs/AccessControl.md b/docs/features/access-control.md similarity index 99% rename from docs/AccessControl.md rename to docs/features/access-control.md index 402f90624..101502566 100644 --- a/docs/AccessControl.md +++ b/docs/features/access-control.md @@ -1,4 +1,4 @@ -# SRT Access Control Guidelines +# SRT Access Control (Stream ID) Guidelines ## Motivation @@ -6,7 +6,7 @@ One type of information that can be interchanged when a connection is being established in SRT is "Stream ID", which can be used in a caller-listener connection layout. This is a string of maximum 512 characters set on the caller side. It can be retrieved at the listener side on the newly accepted socket -through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](API/API-socket-options.md)). +through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](../API/API-socket-options.md)). As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, @@ -121,7 +121,7 @@ to the caller. This specifies that the file is expected to be transmitted from the caller to the listener and its name is `results.csv`. -### Rejection codes +### Rejection Codes The listener callback handler is also able to decide about rejecting the incoming connection. In a normal situation, the rejection code is predefined diff --git a/docs/bonding-intro.md b/docs/features/bonding-intro.md similarity index 99% rename from docs/bonding-intro.md rename to docs/features/bonding-intro.md index 578d644cd..e8c747e5c 100644 --- a/docs/bonding-intro.md +++ b/docs/features/bonding-intro.md @@ -21,7 +21,7 @@ How the links are utilized within a group depends on the group type. The simplest type, broadcast, utilizes all links at once to send the same data. To learn more about socket groups and their abilities, please read the -[detailed document](socket-groups.md). +[SRT Socket Groups](socket-groups.md) document. # Reminder: Using sockets for establishing a connection diff --git a/docs/encryption.md b/docs/features/encryption.md similarity index 95% rename from docs/encryption.md rename to docs/features/encryption.md index 614a11303..60e38dc15 100644 --- a/docs/encryption.md +++ b/docs/features/encryption.md @@ -1,7 +1,11 @@ -# Introduction +# SRT Encryption + +**NOTE:** This document might be outdated, please consult [Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. + This document describes an encryption mechanism that protects the payload of SRT streams. Despite using standard cryptographic algorithms, the mechanism is unique and does not interoperate with any known third party stream encryption method. -### Terminology +## Terminology + | Term | Description | |------|-------------| | AEAD | Authenticated Encryption with Associated Data | @@ -53,7 +57,8 @@ This document describes an encryption mechanism that protects the payload of SRT | TU | Transmission Unit | | UDP | User Datagram Protocol | -### References +## References + * [ANSX9.102] Accredited Standards Committee, Wrapping of Keys and Associated Data, ANS X9.102, not for free document. * [FIPS 140-2] Security Requirements for Cryptographic Modules, NIST, [FIPS PUB 140-2](https://csrc.nist.gov/csrc/media/publications/fips/140/2/final/documents/fips1402.pdf), May 2001. * [FIPS 140-3] Security Requirements for Cryptographic Modules, NIST, [FIPS PUB 140-3](https://csrc.nist.gov/publications/detail/fips/140/3/archive/2009-12-11), December 2009. @@ -71,7 +76,8 @@ This document describes an encryption mechanism that protects the payload of SRT * [RFC6070] PBKDF2 Test Vectors * [SRTP-EKT] Encrypted Key Transport for Secure RTP, D. McGrew, F. Andreasen, D. Wing, K. Fisher, [draft-ietf-avt-srtp-ekt-02](https://tools.ietf.org/html/draft-ietf-avt-srtp-ekt-02), March 2011. -### Operators +## Operators + | Operator | Setting | |----------|---------| | a \|\| b | Concatenation | @@ -84,7 +90,8 @@ This document describes an encryption mechanism that protects the payload of SRT | PRNG(n) | Pseudo Random Number Generator (n bits) | | PBKDF2(p,s,i,l) | Password-based Key Derivation Function (PKCS #5)
p: password, s: salt, i: iterations, l: key length | -# Overview +## Overview + AES in counter mode (AES-CTR) is used with a short lived key to encrypt the media stream. This cipher is suitable for random access of a continuous stream, content protection (used by HDCP 2.0), and strong confidentiality when the counter is managed properly. The short lived key is randomly generated by the sender and transmitted within the stream (KM Tx Period), wrapped with another longer-term key, the Key Encrypting Key (KEK). For connection-oriented transport such as SRT, there is no need to periodically transmit the short lived key since no party can join the stream at any time. @@ -95,48 +102,49 @@ A pre-shared password used with a password-based key derivation mechanism is pro The short lived key, hereafter called the Stream Encrypting Key (SEK), is regenerated for cryptographic reasons when enough packets have been encrypted with it (KM Refresh Rate). To ensure seamless rekeying, the next key to use is transmitted in advance to receivers (KM Pre-Announce) so they can switch keys without disruption when rekeying occurs. KM Refresh Rate and KM Pre-Announce are system parameters that can be configurable options if shorter time than the cryptographic limit is required (for example to limit the material obtained from a compromised SEK). -## Definitions +### Definitions + This section defines the elements of the SRT encryption mechanism. Figure 1 shows the encryption of arbitrary SRT payload. ![Figure 1][figure1] Figure 1 -### Ciphers (AES-CTR) +#### Ciphers (AES-CTR) The payload is encrypted with a cipher in counter mode (AES-CTR). The counter mode is one of the only cipher mode suitable for continuous stream encryption that permits decryption from any point, without access to start of the stream (random access), and for the same reason tolerates packet lost. The Electronic Code Book (ECB) mode also has these characteristics but does not provide serious confidentiality and is not recommended in cryptography. -### Media Stream message (MSmsg) +#### Media Stream message (MSmsg) The Media Stream message is formed from the SRT media stream (data) packets with some elements of the SRT header used for the cryptography. SRT header already carries a 32-bit packet sequence that is used for the cipherā€™s counter (ctr) and 2 bits are stolen from the headerā€™s message number (then reduced to 27-bits) for the encryption key (odd/even) indicator. -### Keying Material +#### Keying Material For each stream, the sender generates a Stream Encrypting Key (SEK) and a Salt (not shown in Figure 1). For the initial implementation and for most envisioned scenarios where no separate authentication algorithm is used for message integrity, the SEK is used directly to encrypt the media stream. The Initial Vector (IV) for the counter is derived from the Salt only. In other scenarios, the SEK can be used along with the Salt as a key generating material to produce distinct encryption, authentication, and salt keys. -### Stream Encrypting Key (SEK) +#### Stream Encrypting Key (SEK) The Stream Encrypting Key (SEK) is pseudo-random and different for each stream. It must be 128, 192, or 256 bits long for the AES-CTR ciphers. It is non-persistent and relatively short lived. In a typical scenario the SEK is expected to last, cryptographically, around 37 days for a 31-bit counter (231 packets / 667 packets/second). The SEK is regenerated every time a stream starts. It must be discarded before 231 packets are encrypted (31-bit packet index) and replaced seamlessly using an odd/even key mechanism described further. SRT is conservative and regenerates the SEK key every 225 packets (~6 hours in the above scenario of a 667 packets per second stream). Reusing an IV (often called nonce) with the same key on different clear text is a known catastrophic issue of counter mode ciphers. By regenerating the SEK each time a stream starts we remove the need for fancier management of the IV to ensure uniqueness. -### Initialization Vector (IV) +#### Initialization Vector (IV) The IV (also named nonce in the AES-CTR context) is a 112 bit random number. For the initial implementation and for most envisioned scenarios where no separate authentication algorithm is used for message integrity (Auth=0), the IV is derived from the salt only. IV = MSB(112, Salt) ; Most significant 112 bits of the salt. -### Counter (ctr) +#### Counter (ctr) The counter for AES-CTR is the size of the cipherā€™s block, i.e. 128 bits. It is made of a block counter in the least significant 16 bits, counting blocks of a packet, and a 32 bits packet index in the next 32 bits. The upper 112 bits are XORed with the IV to produce a unique counter for each crypto block. ![Figure 2][figure2] Figure 2 The block counter (bctr) is incremented for each cipher block while producing the key stream. The packet index is incremented for each packet submitted to the cipher. The IV is derived from the Salt provided with the Keying Material. -### Keying Material message (KMmsg) +#### Keying Material message (KMmsg) The SEK and a Salt are transported in-stream, in a Keying Material message (KMmsg), implemented as a SRT custom control packet, wrapped with a longer term Key Encrypting Key (KEK) using AES key wrap [RFC3394]. There are possibilities for an eventual key wrapper with integrity such as AESKW [ANSX9.102] or AES-SIV [RFC5297]. Transmitting a key in-band is not original to this specification. It is used in DVB MPEG-TS where the stream encrypting key is transmitted in an Entitlement Control Message (ECM). It is also proposed in an IETF draft for SRTP for Encrypted Key Transport [SRTP-EKT]. The connection-oriented SRT KM ctrl packet is transmitted at the start of the connection, before any data packet. In most case, if the control packet is not lost, the receiver is able to decrypt from the first packet. Otherwise, the initial packets are dropped (or stored for later decryption) until the KM control packet is received. The SRT ctrl packet is retransmitted until acknowledged by the receiver. -### Odd/Even Stream Encrypting Key (oSEK/eSEK) +#### Odd/Even Stream Encrypting Key (oSEK/eSEK) To ensure seamless rekeying for cryptographic (counter exhausted) or access control reasons, a two-key mechanism, similar to the one used with DVB systems is used. The two keys are identified as the odd key and the even key (oSEK/eSEK). Basically, an odd/even flag in the SRT data header tells which key is in use. The next key to use is transmitted in advance (KM Pre-Announce) to the receivers in a SRT ctrl packet. When rekeying occurs, the SRT data header odd/even flag flips and the receiver already have the new key in hand to continue decrypting the stream without missing a packet. -### Key Encrypting Key (KEK) +#### Key Encrypting Key (KEK) The KEK is used by the sender to wrap the SEK and by the receiver to unwrap it and then decrypt the stream. The KEK must be at least the size of the key it protects, the SEK. The KEK is derived from a shared secret, a pre-shared password by default. The KEK is derived with the PBKDF2 [PCKS5] derivation function with the stream Salt and the shared secret for input. Each stream then uses a unique KEK to encrypt its Keying Material. A compromised KEK does not compromise other streams protected with the same shared secret (but a compromised shared secret compromises all streams protected with KEK derived from it). Late derivation of the KEK using stream Salt also permits to generate a KEK of the proper size, based on the size of the key it protects. diff --git a/docs/handshake.md b/docs/features/handshake.md similarity index 99% rename from docs/handshake.md rename to docs/features/handshake.md index 756c538e4..ef3fcfdbe 100644 --- a/docs/handshake.md +++ b/docs/features/handshake.md @@ -3,6 +3,8 @@ Published: 2018-06-28 Last updated: 2018-06-28 +**NOTE:** This document might be outdated, please consult [Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and [Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. + **Contents** - [Overview](#overview) @@ -1333,7 +1335,7 @@ account to calculate the time threshold for `TLPKTDROP`. `KMREQ` and `KMRSP` contain the KMX (key material exchange) message used for encryption. The most important part of this message is the -AES-wrapped key (see the [Encryption documentation](encryption.md) for +AES-wrapped key (see [SRT Encryption](encryption.md) for details). If the encryption process on the Responder side was successful, the response contains the same message for confirmation. Otherwise it's one single 32-bit value that contains the value of `SRT_KMSTATE` type, @@ -1540,8 +1542,4 @@ application should set it on a Caller socket using the `SRTO_STREAMID` option. Upon connection, the accepted socket on the Listener side will have exactly the same value set, and it can be retrieved using the same option. For more details about the prospective use of this option, please refer to the -[SRT API Socket Options](API/API-socket-options.md) and [SRT Access Control guidelines](AccessControl.md). - - -[Return to top of page](#srt-handshake) - +[SRT API Socket Options](../API/API-socket-options.md) and [SRT Access Control (Stream ID) Guidlines](access-control.md). diff --git a/docs/images/block-aligned-5rx10c.png b/docs/features/images/block-aligned-5rx10c.png similarity index 100% rename from docs/images/block-aligned-5rx10c.png rename to docs/features/images/block-aligned-5rx10c.png diff --git a/docs/images/block-aligned.png b/docs/features/images/block-aligned.png similarity index 100% rename from docs/images/block-aligned.png rename to docs/features/images/block-aligned.png diff --git a/docs/images/non-block-aligned-5rx10c-deleted-packets.png b/docs/features/images/non-block-aligned-5rx10c-deleted-packets.png similarity index 100% rename from docs/images/non-block-aligned-5rx10c-deleted-packets.png rename to docs/features/images/non-block-aligned-5rx10c-deleted-packets.png diff --git a/docs/images/non-block-aligned-5rx10c.png b/docs/features/images/non-block-aligned-5rx10c.png similarity index 100% rename from docs/images/non-block-aligned-5rx10c.png rename to docs/features/images/non-block-aligned-5rx10c.png diff --git a/docs/images/non-block-aligned.png b/docs/features/images/non-block-aligned.png similarity index 100% rename from docs/images/non-block-aligned.png rename to docs/features/images/non-block-aligned.png diff --git a/docs/images/packet-filter-mechanism.png b/docs/features/images/packet-filter-mechanism.png similarity index 100% rename from docs/images/packet-filter-mechanism.png rename to docs/features/images/packet-filter-mechanism.png diff --git a/docs/images/rebuild-missing-sequence.png b/docs/features/images/rebuild-missing-sequence.png similarity index 100% rename from docs/images/rebuild-missing-sequence.png rename to docs/features/images/rebuild-missing-sequence.png diff --git a/docs/images/srt-encryption-1.png b/docs/features/images/srt-encryption-1.png similarity index 100% rename from docs/images/srt-encryption-1.png rename to docs/features/images/srt-encryption-1.png diff --git a/docs/images/srt-encryption-2.png b/docs/features/images/srt-encryption-2.png similarity index 100% rename from docs/images/srt-encryption-2.png rename to docs/features/images/srt-encryption-2.png diff --git a/docs/images/staircase-pattern-5rx10c.png b/docs/features/images/staircase-pattern-5rx10c.png similarity index 100% rename from docs/images/staircase-pattern-5rx10c.png rename to docs/features/images/staircase-pattern-5rx10c.png diff --git a/docs/live-streaming.md b/docs/features/live-streaming.md similarity index 95% rename from docs/live-streaming.md rename to docs/features/live-streaming.md index 1ee552cc2..f0662b60f 100644 --- a/docs/live-streaming.md +++ b/docs/features/live-streaming.md @@ -1,15 +1,14 @@ -Live streaming with SRT - guidelines -==================================== +## Live Streaming with SRT - Guidelines + +**NOTE:** See also best practices and configuration tips in [Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). SRT is primarily created for live streaming. Before you use it, you must keep in mind that Live Streaming is a process with its own rules, of which SRT fulfills only and exclusively the transmission part. The Live Streaming process consists of more parts. - -Transmitting MPEG TS binary protocol over SRT -============================================= +## Transmitting MPEG TS binary protocol over SRT MPEG-TS is the most important protocol commonly sent over internet using SRT, and the main reason for initiating this project. @@ -30,9 +29,7 @@ an extra header (this is an option that people often try with RTP) - note that However, the transmission must still satisfy the Live Streaming Requirements. - -Live Streaming Requirements -=========================== +## Live Streaming Requirements The MPEG-TS stream, as a good example, consists of Frames. Each Frame is a portion of data assigned to a particular stream (usually you have @@ -92,9 +89,7 @@ requirement for a live stream is that data must be transmitted with exactly the **average** speed as they are output by a video player. More precisely, the data must be produced at exactly the same speed as they will be consumed by the video player. - -Live Streaming Process -====================== +## Live Streaming Process Now that you know how Live Streaming turns a bunch of MPEG-TS encoded video and audio frames into a network live stream, let's complete the definition of live streaming diff --git a/docs/packet-filtering-and-fec.md b/docs/features/packet-filtering-and-fec.md similarity index 98% rename from docs/packet-filtering-and-fec.md rename to docs/features/packet-filtering-and-fec.md index c834d9934..1f1e04bea 100644 --- a/docs/packet-filtering-and-fec.md +++ b/docs/features/packet-filtering-and-fec.md @@ -52,7 +52,7 @@ The packet filter framework is open for extensions so that users may register their own filters. SRT provides also one built-in filter named "fec". This filter implements the FEC mechanism, as described in SMPTE 2022-1-2007. -![SRT packet filter mechanism](/docs/images/packet-filter-mechanism.png) +![SRT packet filter mechanism](images/packet-filter-mechanism.png) On the input side, filtering occurs at the moment when a packet is extracted from the send buffer. A filter may then do two things: @@ -99,12 +99,12 @@ a number of columns in one series) * **even**: block aligned (default) - columns are arranged in a solid matrix; the first sequence numbers (SNbase) are all contained in one row: - ![Block-aligned Example](/docs/images/block-aligned.png) + ![Block-aligned Example](images/block-aligned.png) * **staircase**: non-block aligned - column starting points are staggered; the first sequence numbers (SNbase) have an offset equivalent to R+1: - ![Non-block-aligned Example](/docs/images/non-block-aligned.png) + ![Non-block-aligned Example](images/non-block-aligned.png) * **arq**: Optional use of the Automatic Repeat Request (ARQ) protocol. The @@ -235,7 +235,7 @@ The rows begin with sequences numbers 500, 510, 520, 530 and 540. But the column begin in staggered fashion, separated by an interval of R+1. The colours represent consecutive FEC groups (note the "staircase" pattern): -![5R x 10C Staircase Pattern](/docs/images/staircase-pattern-5rx10c.png) +![5R x 10C Staircase Pattern](images/staircase-pattern-5rx10c.png) Here is a representation of a series of packets transmitted starting from packet 537 (H = horizontal position; V = vertical position): @@ -304,7 +304,7 @@ the example below, it is likely that the entire missing sequence from 572 to groups (once these are rebuilt, it becomes possible to rebuild packets 572 and 582 via row FEC): -![Rebuild Missing Sequence](/docs/images/rebuild-missing-sequence.png) +![Rebuild Missing Sequence](images/rebuild-missing-sequence.png) Although in a case of even arrangement you still may have a good luck of having a long loss exactly at the border of two column series, the staircase @@ -649,7 +649,7 @@ by deleting the entire matrix. In the figure below, with a matrix size of 5 rows by 10 columns, the green region of column and row groups (series 0) is deleted once a packet (#550 or later) from the red region (series 1) arrives. -![Block Aligned 5R x 10C](/docs/images/block-aligned-5rx10c.png) +![Block Aligned 5R x 10C](images/block-aligned-5rx10c.png) For non block-aligned (staircase) FEC arrangements, a series has a more complex @@ -659,7 +659,7 @@ In the figure below (also a matrix size of 5 rows by 10 columns), we can define packet #500 as base0 - the very first sequence number in a group that is still active. Red then represents series 0, blue series 1, and white series 2: -![Non-block Aligned 5R x 10C](/docs/images/non-block-aligned-5rx10c.png) +![Non-block Aligned 5R x 10C](images/non-block-aligned-5rx10c.png) In a staircase arrangement, the minimum distance between base0 and the first @@ -677,7 +677,7 @@ group - that is, all rows whose first packet falls in the sequence from #500 to #540. In the figure below, deletion of series 0 corresponds to columns with a red background and rows with a red border: -![Non-block Aligned 5R x 10C Deleted Packets](/docs/images/non-block-aligned-5rx10c-deleted-packets.png) +![Non-block Aligned 5R x 10C Deleted Packets](images/non-block-aligned-5rx10c-deleted-packets.png) After the red series 0 is deleted, packet #550 becomes the new base0, packets in diff --git a/docs/socket-groups.md b/docs/features/socket-groups.md similarity index 100% rename from docs/socket-groups.md rename to docs/features/socket-groups.md diff --git a/docs/gstreamer.md b/docs/gstreamer.md deleted file mode 100644 index f02086e5f..000000000 --- a/docs/gstreamer.md +++ /dev/null @@ -1,62 +0,0 @@ -# Using SRT with GStreamer - -Starting from ver. 1.14 GStreamer supports SRT (see the [v.1.14 release notes](https://gstreamer.freedesktop.org/releases/1.14/)). -See the SRT plugin for GStreamer on [git](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/tree/master/ext/srt). - - -## Using GStreamer and SRT to set up a screensharing - -Based on the description in [#7](https://github.com/Haivision/srt/issues/7). Note that the commands are likely to change slightly for gstreamer 1.16 (see this [issue](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/874#note_106395)). - -If you don't want to build GSteamer, SRT, and all the plugins from source or don't have a distribution that has 1.14 readily available, you can use [`nix`](https://nixos.org/nix/) to reproduce what is shown further. - -Simply install `nix`; then use the command bellow to open a shell where the following commands work. - -``` -NIX_PATH=nixpkgs=https://github.com/nh2/nixpkgs/archive/a94ff5f6aaa.tar.gz nix-shell -p gst_all_1.gstreamer \ --p gst_all_1.gst-plugins-good -p gst_all_1.gst-plugins-base -p gst_all_1.gst-plugins-bad \ --p gst_all_1.gst-plugins-ugly -p gst_all_1.gst-libav -``` - -### Sender server - -Set up a sender server that will grab a source raw video from a desktop or a webcam, encode it with x.264 (H.264/AVC) encoder, pack it in `MPEG-TS` ([more info about live streaming](live-streaming.md)). Then pipe it into the SRT sink that sends it over the network to the receiver client. The streaming URI should looks like `uri=srt://:`. In the examples below the streaming is sent to port 888 on a localhost by specifying `uri=srt://0.0.0.0:8888`. - - -##### For screensharing (Linux with X Display) - -The `ximagesrc` GStreamer plugin can be used to capture X Display and create raw RGB video. -Refer to `ximagesrc` [RM](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-ximagesrc.html) for configuration options. - -``` -/usr/bin/time gst-launch-1.0 ximagesrc startx=0 show-pointer=true use-damage=0 ! videoconvert \ -! x264enc bitrate=32000 tune=zerolatency speed-preset=veryfast byte-stream=true threads=1 key-int-max=15 \ -intra-refresh=true ! video/x-h264, profile=baseline, framerate=30/1 ! mpegtsmux \ -! srtserversink uri=srt://0.0.0.0:8888/ latency=100 -``` - -##### For webcam images - -The `v4l2src` GStreamer plugin can be used to capture video from v4l2 devices, like webcams and TV cards. -Refer to `v4l2src` [RM](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-v4l2src.html) for further information. - -``` -/usr/bin/time gst-launch-1.0 v4l2src ! videoconvert ! x264enc bitrate=8000 tune=zerolatency speed-preset=superfast \ -byte-stream=true threads=1 key-int-max=15 intra-refresh=true ! video/x-h264, profile=baseline ! mpegtsmux \ -! srtserversink uri=srt://0.0.0.0:8888/ latency=100 -``` - -##### Notes - -* The `decodebin` can also be used to configure settings automatically. Using explicit pipeline elements here make it possible to tune the settings when needed. -* A use of `time` helps to determine when the thread is capped at 100%, while the the `thread=1` parameter makes the encoding use only one thread. Remove `threads=1` to allow multiple cores, or cjange the `speed-preset` to reduce CPU load. -* The `timeout` setting can be tuned. A recommended timeout is 2x-2.5x of the expected roundtrip time. -* The password functionality works as well, but only if a password is `>= 10` characters long; otherwise it's completely ignored. See [this bug](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/694#note_106616) of GStreamer. - -### Receiver client - -A client connection over SRT to the server with URI `srt://127.0.0.1:8888` (localhost) or a remote server is set up. URI syntax is `srt://:`. Then MPEG-TS demuxer and video decoder is used to get a decompressed video, that goes to a playback plugin `autovideosink`. Note that multiple clients can connect to the server started earlier. - -`gst-launch-1.0 srtclientsrc uri=srt://127.0.0.1:8888 ! tsdemux ! h264parse ! video/x-h264 ! avdec_h264 ! autovideosink sync=false` - -This works over both the internet and localhost. diff --git a/docs/images/SRT_History_Good_Signal.png b/docs/misc/images/srt-history-good-signal.png similarity index 100% rename from docs/images/SRT_History_Good_Signal.png rename to docs/misc/images/srt-history-good-signal.png diff --git a/docs/images/SRT_Transmission_Bad_Signal.png b/docs/misc/images/srt-transmission-bad-signal.png similarity index 100% rename from docs/images/SRT_Transmission_Bad_Signal.png rename to docs/misc/images/srt-transmission-bad-signal.png diff --git a/docs/why-srt-was-created.md b/docs/misc/why-srt-was-created.md similarity index 97% rename from docs/why-srt-was-created.md rename to docs/misc/why-srt-was-created.md index 0d3e35e23..cc694e5df 100644 --- a/docs/why-srt-was-created.md +++ b/docs/misc/why-srt-was-created.md @@ -15,14 +15,14 @@ Having had a history with UDT for data transmission, I remembered its packet los We started testing sending low latency live streams back and forth between Germany and Montreal and it worked! However, we didn't get the latency down to a level we had hoped to achieve. The problem we faced turned out to be timing related (as always in media ...). What happened was this: -![Bad Signal](images/SRT_Transmission_Bad_Signal.png) +![Bad Signal](images/srt-transmission-bad-signal.png) The characteristics of the original stream on the source network got completely changed by the transmission over the public internet. The reasons are delay, jitter, packet loss and its recovery on the dirty network. The signal on the receiver side had completely different characteristics, which led to problems with decoding, as the audio and video decoders didn't get the packets at the expected times. This can be handled by buffering, but that's not what you want in low latency setups. The solution was to come up with a mechanism that recreates the signal characteristics on the receiver side. That way we were able to dramatically reduce the buffering. This functionality is part of the SRT protocol itself, so once the data comes out of the SRT protocol on the receiver side, the stream characteristics have been properly recovered. The result is a happy decoder: -![Good Signal](images/SRT_History_Good_Signal.png) +![Good Signal](images/srt-history-good-signal.png) We publicly showed SRT (Secure Reliable Transport) the first time at IBC 2013, where we were the only ones to show an HEVC encoded live stream, camera to glass, from a hotel suite outside the exhibition directly onto the show floor, using the network provided by the RAI. Everybody who has been at such a show before knows how bad these networks can get. And the network was bad. So bad that we expected the whole demo to fall apart, having pulled the first trial version of SRT directly from the labs. The excitement was huge, when we realized that the transmission still worked fine! diff --git a/scripts/build-android/README.md b/scripts/build-android/README.md new file mode 100644 index 000000000..d225ed022 --- /dev/null +++ b/scripts/build-android/README.md @@ -0,0 +1,5 @@ +## Scripts for building SRT for Android + +**NOTE:** The scripts have been moved from `docs/Android/` folder. Updating the paths might be required. + +See [Building SRT for Android](../../docs/build/build-android.md) for the instructions. diff --git a/docs/Android/mkall b/scripts/build-android/mkall similarity index 100% rename from docs/Android/mkall rename to scripts/build-android/mkall diff --git a/docs/Android/mksrt b/scripts/build-android/mksrt similarity index 100% rename from docs/Android/mksrt rename to scripts/build-android/mksrt diff --git a/docs/Android/mkssl b/scripts/build-android/mkssl similarity index 100% rename from docs/Android/mkssl rename to scripts/build-android/mkssl diff --git a/docs/Android/packjni b/scripts/build-android/packjni similarity index 100% rename from docs/Android/packjni rename to scripts/build-android/packjni diff --git a/docs/Android/prepare_build b/scripts/build-android/prepare_build similarity index 100% rename from docs/Android/prepare_build rename to scripts/build-android/prepare_build diff --git a/srtcore/api.h b/srtcore/api.h index cd467a978..604f14a99 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -70,7 +70,7 @@ modified by #endif // Please refer to structure and locking information provided in the -// LowLevelInfo.md document. +// docs/dev/low-level-info.md document. class CUDT; From 98649a6457a80dca28d6cfe5165721d9c6a7072d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 17:15:20 +0200 Subject: [PATCH 127/790] [core] Fixed SRTO_KM* options setting. (#1922) - Rejecting negative values of SRTO_KM* in srt_setsockopt. - Handling zero value properly (as the default). - SRTO_KM* options are now readable as was stated in docs. - Updated docs. - Removed unused func argument (processCtrlShutdown). --- docs/API/API-socket-options.md | 144 +++++++++++++++++---------------- srtcore/core.cpp | 14 +++- srtcore/core.h | 4 +- srtcore/socketconfig.h | 40 ++++++--- 4 files changed, 119 insertions(+), 83 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2680e060f..c1d4420e1 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -193,67 +193,67 @@ The + marker can only coexist with GS. Possible specifications are: The following table lists SRT API socket options in alphabetical order. Option details are given further below. -| Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | -| :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| -| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | -| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | -| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | -| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | -| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | -| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | -| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | -| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | -| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | -| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | -| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | -| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000000 | 0.. | RW | GSD | -| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | -| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | -| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | -| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | -| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | -| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | -| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | -| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | -| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | -| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | -| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | -| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | -| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | -| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | -| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | -| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | -| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | -| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | +| Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | +| :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :---------------: | :------: |:---:|:-----:| +| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | +| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | +| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | +| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | +| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | +| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | +| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | +| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | +| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | +| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | +| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | +| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | +| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | +| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | +| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | +| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | +| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | +| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | +| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | +| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | +| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | +| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | +| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | +| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | +| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | +| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | +| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | +| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | +| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | +| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | +| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | +| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | +| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | +| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | +| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | +| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | +| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | +| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | +| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions @@ -623,9 +623,9 @@ used in any regular development.* #### SRTO_KMPREANNOUNCE -| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | +| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | The interval (defined in packets) between when a new Stream Encrypting Key (SEK) is sent and when switchover occurs. This value also applies to the @@ -642,20 +642,24 @@ retransmitted packets. The old key is decommissioned at `SRTO_KMPREANNOUNCE` packets after switchover. -The allowed range for this value is between 1 and half of the current value of +**The allowed range** for this value is between 1 and half of the current value of `SRTO_KMREFRESHRATE`. The minimum value should never be less than the flight -window (i.e. the number of packets that have already left the sender but have +window [`SRTO_FC`](#SRTO_FC) (i.e. the number of packets that have already left the sender but have not yet arrived at the receiver). +The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. + +**Default value:** `0` - corresponds to 4096 packets (212 or 0x1000). + [Return to list](#list-of-options) --- #### SRTO_KMREFRESHRATE -| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000000| 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | +| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0: 224| 0.. | RW | GSD | The number of packets to be transmitted after which the Stream Encryption Key (SEK), used to encrypt packets, will be switched to the new one. Note that @@ -667,6 +671,8 @@ at the receiver before the first packet encrypted with the new SEK is received. The old key remains active after switchover in order to decrypt packets that might still be in flight, or packets that have to be retransmitted. +**Default value:** `0` - corresponds to 16777216 packets (224 or 0x1000000). + [Return to list](#list-of-options) --- diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 381669b30..a3d6b217f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -708,6 +708,16 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) *(int *)optval = (int) m_config.zExpPayloadSize; break; + case SRTO_KMREFRESHRATE: + optlen = sizeof(int); + *(int*)optval = (int)m_config.uKmRefreshRatePkt; + break; + + case SRTO_KMPREANNOUNCE: + optlen = sizeof(int); + *(int*)optval = (int)m_config.uKmPreAnnouncePkt; + break; + #if ENABLE_EXPERIMENTAL_BONDING case SRTO_GROUPCONNECT: optlen = sizeof (int); @@ -8399,7 +8409,7 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) +void CUDT::processCtrlShutdown() { m_bShutdown = true; m_bClosing = true; @@ -8486,7 +8496,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - processCtrlShutdown(ctrlpkt); + processCtrlShutdown(); break; case UMSG_DROPREQ: // 111 - Msg drop request diff --git a/srtcore/core.h b/srtcore/core.h index 6be1a9011..982408c8b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -970,9 +970,7 @@ class CUDT void processCtrlDropReq(const CPacket& ctrlpkt); /// @brief Process incoming shutdown control packet - /// @param ctrlpkt incoming shutdown packet - void processCtrlShutdown(const CPacket& ctrlpkt); - + void processCtrlShutdown(); /// @brief Process incoming user defined control packet /// @param ctrlpkt incoming user defined packet void processCtrlUserDefined(const CPacket& ctrlpkt); diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 29105808b..969d624e5 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -1002,14 +1002,28 @@ struct CSrtConfigSetter { using namespace srt_logging; - // If you first change the KMREFRESHRATE, KMPREANNOUNCE - // will be set to the maximum allowed value - co.uKmRefreshRatePkt = cast_optval(optval, optlen); - if (co.uKmPreAnnouncePkt == 0 || co.uKmPreAnnouncePkt > (co.uKmRefreshRatePkt - 1) / 2) + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value + co.uKmRefreshRatePkt = (unsigned) val; + + if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) + return; // Both values are default + + const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; + const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + + if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) { - co.uKmPreAnnouncePkt = (co.uKmRefreshRatePkt - 1) / 2; + co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << co.uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" + log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" << std::hex << co.uKmPreAnnouncePkt); } } @@ -1023,11 +1037,19 @@ struct CSrtConfigSetter using namespace srt_logging; const int val = cast_optval(optval, optlen); - const int kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - if (val > (kmref - 1) / 2) + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; + const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } From c3864aac7cbf4ee07a3e4948e04274cd577e59da Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Mon, 12 Apr 2021 03:49:26 -0500 Subject: [PATCH 128/790] [apps] Fix android build NDK r16b and earlier. (#1923) --- apps/apputil.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 6031668ba..f7bf83df3 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -58,6 +58,19 @@ inline void SysCleanupNetwork() #include #include +// Fixes Android build on NDK r16b and earlier. +#if defined(__ANDROID__) && (__ANDROID__ == 1) + #include + #if !defined(__NDK_MAJOR__) || (__NDK_MAJOR__ <= 16) + struct ip_mreq_sourceFIXED { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; + struct in_addr imr_sourceaddr; + }; + #define ip_mreq_source ip_mreq_sourceFIXED + #endif +#endif + // Nothing needs to be done on POSIX; this is a Windows problem. inline bool SysInitializeNetwork() {return true;} inline void SysCleanupNetwork() {} From e63edadca6559dc7e79fa4ddcc8217173e124574 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 31 Mar 2021 16:31:48 +0800 Subject: [PATCH 129/790] [build] Fix pc file generation when use mbedtls mbedtls doesn't create .pc file by itself. Put mbedtls under Requires.private leads to pkg-config check error. --- CMakeLists.txt | 51 ++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c37bdc41..25f34c881 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,14 +283,29 @@ if (ENABLE_ENCRYPTION) link_directories( ${SSL_LIBRARY_DIRS} ) - else() # Common for mbedtls and openssl - if ("${USE_ENCLIB}" STREQUAL "mbedtls") - add_definitions(-DUSE_MBEDTLS=1) - set (SSL_REQUIRED_MODULES "mbedtls mbedcrypto") - else() - add_definitions(-DUSE_OPENSSL=1) - set (SSL_REQUIRED_MODULES "openssl libcrypto") + elseif ("${USE_ENCLIB}" STREQUAL "mbedtls") + add_definitions(-DUSE_MBEDTLS=1) + if ("${SSL_LIBRARY_DIRS}" STREQUAL "") + set(MBEDTLS_PREFIX "${CMAKE_PREFIX_PATH}" CACHE PATH "The path of mbedtls") + find_package(MbedTLS REQUIRED) + set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) + endif() + if ("${SSL_LIBRARIES}" STREQUAL "") + set (SSL_LIBRARIES mbedtls mbedcrypto) endif() + message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") + + foreach(LIB ${SSL_LIBRARIES}) + if(IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) + set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${LIB}) + else() + set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} "-l${LIB}") + endif() + endforeach() + else() # openssl + add_definitions(-DUSE_OPENSSL=1) + set (SSL_REQUIRED_MODULES "openssl libcrypto") # Try using pkg-config method first if enabled, # fall back to find_package method otherwise if (USE_OPENSSL_PC) @@ -315,24 +330,12 @@ if (ENABLE_ENCRYPTION) ) message(STATUS "SSL via pkg-config: -L ${SSL_LIBRARY_DIRS} -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") else() - if ("${USE_ENCLIB}" STREQUAL "mbedtls") - if ("${SSL_LIBRARY_DIRS}" STREQUAL "") - set(MBEDTLS_PREFIX "${CMAKE_PREFIX_PATH}" CACHE PATH "The path of mbedtls") - find_package(MbedTLS REQUIRED) - set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) - endif() - if ("${SSL_LIBRARIES}" STREQUAL "") - set (SSL_LIBRARIES mbedtls mbedcrypto) - endif() - message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") - else() - find_package(OpenSSL REQUIRED) - set (SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) - set (SSL_LIBRARIES ${OPENSSL_LIBRARIES}) - message(STATUS "SSL via find_package(OpenSSL): -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") - endif() + find_package(OpenSSL REQUIRED) + set (SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) + set (SSL_LIBRARIES ${OPENSSL_LIBRARIES}) + message(STATUS "SSL via find_package(OpenSSL): -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") endif() + endif() add_definitions(-DSRT_ENABLE_ENCRYPTION) From 009b7b62f793718f621050654beb080d644894c8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 13:04:00 +0200 Subject: [PATCH 130/790] [core] Fixed SRTO_SNDDROPDELAY: use POST restriction --- apps/socketoptions.hpp | 2 +- srtcore/core.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 2a14c3854..06c929896 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -229,7 +229,7 @@ const SocketOption srt_options [] { { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "tlpktdrop", 0, SRTO_TLPKTDROP, SocketOption::PRE, SocketOption::BOOL, nullptr}, - { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::PRE, SocketOption::INT, nullptr}, + { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::POST, SocketOption::INT, nullptr}, { "nakreport", 0, SRTO_NAKREPORT, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, { "drifttracer", 0, SRTO_DRIFTTRACER, SocketOption::POST, SocketOption::BOOL, nullptr}, diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a3d6b217f..795f0a727 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -161,19 +161,19 @@ struct SrtOptionAction flags[SRTO_SENDER] = SRTO_R_PRE; flags[SRTO_TSBPDMODE] = SRTO_R_PRE; flags[SRTO_LATENCY] = SRTO_R_PRE; - flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_MININPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_INPUTBW] = SRTO_POST_SPEC; + flags[SRTO_MININPUTBW] = SRTO_POST_SPEC; + flags[SRTO_OHEADBW] = SRTO_POST_SPEC; flags[SRTO_PASSPHRASE] = SRTO_R_PRE; flags[SRTO_PBKEYLEN] = SRTO_R_PRE; flags[SRTO_IPTTL] = SRTO_R_PREBIND; flags[SRTO_IPTOS] = SRTO_R_PREBIND; flags[SRTO_TLPKTDROP] = SRTO_R_PRE; - flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; + flags[SRTO_SNDDROPDELAY] = SRTO_POST_SPEC; flags[SRTO_NAKREPORT] = SRTO_R_PRE; flags[SRTO_VERSION] = SRTO_R_PRE; flags[SRTO_CONNTIMEO] = SRTO_R_PRE; - flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; + flags[SRTO_LOSSMAXTTL] = SRTO_POST_SPEC; flags[SRTO_RCVLATENCY] = SRTO_R_PRE; flags[SRTO_PEERLATENCY] = SRTO_R_PRE; flags[SRTO_MINVERSION] = SRTO_R_PRE; From 7809e20da43755417e17ca231afb17e121b0d651 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 14:32:43 +0200 Subject: [PATCH 131/790] [build] Updated Travis OSX to 11.1 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82c16d99c..c2e2205c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ addons: token: secure: "wJZC0kyyjuf4SZyonZ6p/5Ga9asEqSnKWF9NpRbu6S6ceERO7vbebuSJF5qX3A6ivPuw0TTk5WASOdnvIyfA28FU/D0MWRdH8K7T3w77wdE9EgAEYTUXzdrbzJY18+9pxjljHwWXWALPSGf3MClg4irWrdk1e6uHK+68R39+ZvBGBFpWeeZy/+at9+xwhtAGKBlSHe8zc+3wPxuYdvviLVJ25qbpNmnzkUR0X89G+UBl90raCPSN32EHFdImHZ5DxfEQQJgZFRjzQUY4EW/iYwaMel7jufAq0ClgV4psKujl9Lz8cPqx3WgqRfJyiIthOMTsac7G4zAw8LK2CI0VsssBp0JalLXaumi6vG7o6c3rIwKckzSKccq3pHa7h45praIVVn9s3nq+Q/JGA11FMkKQxdQtmwgFsLhbi6ZxabgsUi5KtWoWY2z6MgpJuROuAjNxZi9XJzUoJs7zSTUtRRW7V8Q2lRiOnknYh25N6TCA5bpyy1EZmRdJErm071YNI9P01gbFz5137FWJFiJzro9TGF0KoHSGiCIdUt3WlMzwr/i/wFLxFBQOZQ2rjTXvhs4hxONxMZV3gzxA1NdLaf9i5Mh6jxVMV+ujaRSV7JmPGzxqiAlpT9cJUhTCYuar9diLLeDrpe7RawEZR8V1xVDQ7yT8ruDNQ78VbSn/sC0=" homebrew: - update: true # TODO: this should be removed once this bug is fixed: https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296 + update: false packages: - openssl @@ -32,10 +32,10 @@ matrix: - os: linux env: BUILD_TYPE=Release - os: osx - osx_image: xcode10.2 + osx_image: xcode11.1 env: BUILD_TYPE=Debug - os: osx - osx_image: xcode10.2 + osx_image: xcode11.1 env: BUILD_TYPE=Release - os: linux compiler: x86_64-w64-mingw32-g++ From 66f2390ca04deea2626700247994ac2dac82d254 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 14:26:35 +0200 Subject: [PATCH 132/790] [tests] Removed "" _S function. On some platforms _S is defined as a macro. --- test/test_ipv6.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 2ca256f1d..677cae180 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -4,11 +4,6 @@ #include "srt.h" #include "netinet_any.h" -inline std::string operator "" _S(const char* src, std::size_t) -{ - return std::string(src); -} - class TestIPv6 : public ::testing::Test { @@ -94,12 +89,12 @@ class TestIPv6 void PrintAddresses(SRTSOCKET sock, const char* who) { sockaddr_any sa; - int sa_len = sa.storage_size(); + int sa_len = (int) sa.storage_size(); srt_getsockname(sock, sa.get(), &sa_len); ShowAddress(std::string(who) + " Sock name: ", sa); //std::cout << who << " Sock name: " << << sa.str() << std::endl; - sa_len = sa.storage_size(); + sa_len = (int) sa.storage_size(); srt_getpeername(sock, sa.get(), &sa_len); //std::cout << who << " Peer name: " << << sa.str() << std::endl; ShowAddress(std::string(who) + " Peer name: ", sa); @@ -121,10 +116,10 @@ TEST_F(TestIPv6, v4_calls_v6_mapped) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200"); client.join(); } @@ -138,10 +133,10 @@ TEST_F(TestIPv6, v6_calls_v6_mapped) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::1:4200"); client.join(); } @@ -158,10 +153,10 @@ TEST_F(TestIPv6, v6_calls_v6) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::1:4200"); client.join(); } @@ -177,10 +172,10 @@ TEST_F(TestIPv6, v6_calls_v4) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200"); client.join(); } From 6a77c0470352f47accd3ceab0357c9173b691823 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 13 Apr 2021 16:47:43 +0800 Subject: [PATCH 133/790] [build] Fix cmake warning on FindMbedTLS (#1935) The warning message: "The package name passed to `find_package_handle_standard_args` (Libmbedtls) does not match the name of the calling package (MbedTLS). This can lead to problems in calling code that expects `find_package` result variables (e.g., `_FOUND`) to follow a certain pattern." --- scripts/FindMbedTLS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/FindMbedTLS.cmake b/scripts/FindMbedTLS.cmake index 7dca6e47e..662255622 100644 --- a/scripts/FindMbedTLS.cmake +++ b/scripts/FindMbedTLS.cmake @@ -111,5 +111,5 @@ endif() # Now we've accounted for the 3-vs-1 library case: include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libmbedtls DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) +find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) From 0f4c32e40a250db9863b6bdaa09a64f8f871f307 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 13 Apr 2021 10:48:03 +0200 Subject: [PATCH 134/790] [apps] Changed the default log level to Warn (#1934) --- apps/srt-live-transmit.cpp | 4 ++-- docs/apps/srt-live-transmit.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 4288f351e..bfe4ff590 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -290,7 +290,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) PrintOptionHelp(o_statsout, "", "output stats to file"); PrintOptionHelp(o_statspf, "", "stats printing format {json, csv, default}"); PrintOptionHelp(o_statsfull, "", "full counters in stats-report (prints total statistics)"); - PrintOptionHelp(o_loglevel, "", "log level {fatal,error,info,note,warning}"); + PrintOptionHelp(o_loglevel, "", "log level {fatal,error,warn,note,info,debug}"); PrintOptionHelp(o_logfa, "", "log functional area (see '-h logging' for more info)"); //PrintOptionHelp(o_log_internal, "", "use internal logger"); PrintOptionHelp(o_logfile, "", "write logs to file"); @@ -345,7 +345,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) } cfg.full_stats = OptionPresent(params, o_statsfull); - cfg.loglevel = SrtParseLogLevel(Option(params, "error", o_loglevel)); + cfg.loglevel = SrtParseLogLevel(Option(params, "warn", o_loglevel)); cfg.logfas = SrtParseLogFA(Option(params, "", o_logfa)); cfg.log_internal = OptionPresent(params, o_log_internal); cfg.logfile = Option(params, o_logfile); diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index a124c631c..184bb32a7 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -369,7 +369,7 @@ shell (using **"** **"** quotes or backslash). - **-statsout**Ā - SRT statistics output: filename. Without this option specified, the statistics will be printed to the standard output. - **-pf**, **-statspf**Ā - SRT statistics print format. Values:Ā json,Ā csv,Ā default. After a comma, options can be specified (e.g. "json,pretty"). - **-s**, **-stats**, **-stats-report-frequency**Ā - The frequency of SRT statistics collection, based on the number of packets. -- **-loglevel** - lowest logging level for SRT, one of: *fatal, error, warning, note, debug* (default: *error*) +- **-loglevel** - lowest logging level for SRT, one of: *fatal, error, warn, note, debug* (default: *warn*) - **-logfa, -lfa** - selected FAs in SRT to be logged (default: all are enabled). See the list of FAs running `-help:logging`. - **-logfile:logs.txt** - Output of logs is written to file logs.txt instead of being printed to `stderr`. - **-help, -h** - Show help. From 65d5483162d3df4d974d93592b2d8df435c7a658 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 13 Apr 2021 17:13:58 +0800 Subject: [PATCH 135/790] [core] Use time_point in debugTraceJitter(..) (#1912) --- srtcore/buffer.cpp | 18 +++++++++--------- srtcore/buffer.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 89c2dab72..5ca443485 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -2064,15 +2064,15 @@ int CRcvBuffer::readMsg(char* data, int len, SRT_MSGCTRL& w_msgctl, int upto) } #ifdef SRT_DEBUG_TSBPD_OUTJITTER -void CRcvBuffer::debugTraceJitter(int64_t rplaytime) +void CRcvBuffer::debugTraceJitter(time_point playtime) { - uint64_t now = CTimer::getTime(); - if ((now - rplaytime) / 10 < 10) - m_ulPdHisto[0][(now - rplaytime) / 10]++; - else if ((now - rplaytime) / 100 < 10) - m_ulPdHisto[1][(now - rplaytime) / 100]++; - else if ((now - rplaytime) / 1000 < 10) - m_ulPdHisto[2][(now - rplaytime) / 1000]++; + uint64_t ms = count_microseconds(steady_clock::now() - playtime); + if (ms / 10 < 10) + m_ulPdHisto[0][ms / 10]++; + else if (ms / 100 < 10) + m_ulPdHisto[1][ms / 100]++; + else if (ms / 1000 < 10) + m_ulPdHisto[2][ms / 1000]++; else m_ulPdHisto[3][1]++; } @@ -2105,7 +2105,7 @@ bool CRcvBuffer::accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playt // so in one "unit". w_p = w_q = m_iStartPos; - debugTraceJitter(w_playtime); + debugTraceJitter(play_time); } } else diff --git a/srtcore/buffer.h b/srtcore/buffer.h index f0a499fd4..d998fd1b9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -380,10 +380,10 @@ class CRcvBuffer bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); #ifdef SRT_DEBUG_TSBPD_OUTJITTER - void debugTraceJitter(int64_t); + void debugTraceJitter(time_point t); #else - void debugTraceJitter(int64_t) {} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + void debugTraceJitter(time_point) {} +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } From 9e6c90f8cca832b866620959bb3407ef02cdfddb Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:17:39 +0200 Subject: [PATCH 136/790] [core] Minor refactoring around ACK processing (#1928) --- srtcore/core.cpp | 38 +++++++++++++++------------- srtcore/core.h | 66 +++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 795f0a727..d73acfc26 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -221,7 +221,9 @@ void CUDT::construct() m_pSndLossList = NULL; m_pRcvLossList = NULL; m_iReorderTolerance = 0; - m_iConsecEarlyDelivery = 0; // how many times so far the packet considered lost has been received before TTL expires + // How many times so far the packet considered lost has been received + // before TTL expires. + m_iConsecEarlyDelivery = 0; m_iConsecOrderedDelivery = 0; m_pSndQueue = NULL; @@ -229,7 +231,8 @@ void CUDT::construct() m_pSNode = NULL; m_pRNode = NULL; - m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; // Will be reset to 0 for HSv5, this value is important for HSv4 + // Will be reset to 0 for HSv5, this value is important for HSv4. + m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; // Initial status m_bOpened = false; @@ -239,12 +242,12 @@ void CUDT::construct() m_bClosing = false; m_bShutdown = false; m_bBroken = false; - // XXX m_iBrokenCounter should be still set to some default! + // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; m_tsLastReqTime = steady_clock::time_point(); m_SrtHsSide = HSD_DRAW; - m_uPeerSrtVersion = 0; // not defined until connected. + m_uPeerSrtVersion = 0; // Not defined until connected. m_iTsbPdDelay_ms = 0; m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; @@ -254,10 +257,10 @@ void CUDT::construct() m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; - // Initilize mutex and condition variables + // Initilize mutex and condition variables. initSynch(); - // XXX: Unblock, when the callback is implemented + // TODO: Uncomment when the callback is implemented. // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } @@ -5433,17 +5436,18 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // And of course, it is connected. m_bConnected = true; - // register this socket for receiving data packets + // Register this socket for receiving data packets. m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); // Save the handshake in m_ConnRes in case when needs repeating. m_ConnRes = w_hs; - // send the response to the peer, see listen() for more discussions about this - // XXX Here create CONCLUSION RESPONSE with: + // Send the response to the peer, see listen() for more discussions + // about this. + // TODO: Here create CONCLUSION RESPONSE with: // - just the UDT handshake, if HS_VERSION_UDT4, - // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP + // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP. size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. @@ -7711,8 +7715,8 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) if (m_uPeerSrtVersion == SrtVersion(1, 0, 2)) { data[ACKD_RCVRATE] = rcvRate; // bytes/sec - data[ACKD_XMRATE] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec - ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102; + data[ACKD_XMRATE_VER102_ONLY] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec + ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102_ONLY; } else if (m_uPeerSrtVersion >= SrtVersion(1, 0, 3)) { @@ -8001,10 +8005,10 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point * Additional UDT fields, not always attached: * ACKD_RCVSPEED * ACKD_BANDWIDTH - * SRT extension version 1.0.2 (bstats): + * SRT extension since v1.0.1: * ACKD_RCVRATE - * SRT extension version 1.0.4: - * ACKD_XMRATE + * SRT extension in v1.0.2 only: + * ACKD_XMRATE_VER102_ONLY */ if (acksize > ACKD_TOTAL_SIZE_SMALL) @@ -8014,7 +8018,7 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point int bandwidth = ackdata[ACKD_BANDWIDTH]; int bytesps; - /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in + /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE_VER102_ONLY]) and delivery rate (pcData[ACKD_RCVRATE]) in * bytes/sec instead of pkts/sec */ /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */ if (acksize > ACKD_TOTAL_SIZE_UDTBASE) @@ -8025,8 +8029,6 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth); m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps); m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps); - // XXX not sure if ACKD_XMRATE is of any use. This is simply - // calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize. // Update Estimated Bandwidth and packet delivery rate // m_iRcvRate = m_iDeliveryRate; diff --git a/srtcore/core.h b/srtcore/core.h index 982408c8b..70dbb2f7d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -76,7 +76,7 @@ modified by #include -// XXX Utility function - to be moved to utilities.h? +// TODO: Utility function - to be moved to utilities.h? template inline T CountIIR(T base, T newval, double factor) { @@ -87,32 +87,30 @@ inline T CountIIR(T base, T newval, double factor) return base+T(diff*factor); } -// XXX Probably a better rework for that can be done - this can be -// turned into a serializable structure, just like it's for CHandShake. +// TODO: Probably a better rework for that can be done - this can be +// turned into a serializable structure, just like it's done for CHandShake. enum AckDataItem { - ACKD_RCVLASTACK = 0, - ACKD_RTT = 1, - ACKD_RTTVAR = 2, - ACKD_BUFFERLEFT = 3, - ACKD_TOTAL_SIZE_SMALL = 4, - - // Extra fields existing in UDT (not always sent) - - ACKD_RCVSPEED = 4, // length would be 16 - ACKD_BANDWIDTH = 5, - ACKD_TOTAL_SIZE_UDTBASE = 6, // length = 24 - // Extra stats for SRT - - ACKD_RCVRATE = 6, - ACKD_TOTAL_SIZE_VER101 = 7, // length = 28 - ACKD_XMRATE = 7, // XXX This is a weird compat stuff. Version 1.1.3 defines it as ACKD_BANDWIDTH*m_iMaxSRTPayloadSize when set. Never got. - // XXX NOTE: field number 7 may be used for something in future, need to confirm destruction of all !compat 1.0.2 version - - ACKD_TOTAL_SIZE_VER102 = 8, // 32 -// FEATURE BLOCKED. Probably not to be restored. -// ACKD_ACKBITMAP = 8, - ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102 // length = 32 (or more) + ACKD_RCVLASTACK = 0, + ACKD_RTT = 1, + ACKD_RTTVAR = 2, + ACKD_BUFFERLEFT = 3, + ACKD_TOTAL_SIZE_SMALL = 4, // Size of the Small ACK, packet length = 16. + + // Extra fields for Full ACK. + ACKD_RCVSPEED = 4, + ACKD_BANDWIDTH = 5, + ACKD_TOTAL_SIZE_UDTBASE = 6, // Packet length = 24. + + // Extra stats since SRT v1.0.1. + ACKD_RCVRATE = 6, + ACKD_TOTAL_SIZE_VER101 = 7, // Packet length = 28. + + // Only in SRT v1.0.2. + ACKD_XMRATE_VER102_ONLY = 7, + ACKD_TOTAL_SIZE_VER102_ONLY = 8, // Packet length = 32. + + ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102_ONLY // The maximum known ACK length is 32 bytes. }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); @@ -267,14 +265,15 @@ class CUDT // Parameters // - // Note: use notation with X*1000*1000*... instead of million zeros in a row - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; - static const size_t ACK_WND_SIZE = 1024; + // NOTE: Use notation with X*1000*1000*... instead of + // million zeros in a row. + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; int handshakeVersion() { @@ -745,7 +744,6 @@ class CUDT int m_iDeliveryRate; // Packet arrival rate at the receiver side int m_iByteDeliveryRate; // Byte arrival rate at the receiver side - CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state From 109f667cf42b2ba7311000ff2c551b72a4069f80 Mon Sep 17 00:00:00 2001 From: quink-black Date: Wed, 14 Apr 2021 20:58:37 +0800 Subject: [PATCH 137/790] [core] Minor: file scope for global var and func (#1938) 1. sync_posix: make global variable and function file scope 2. replace static_assert by SRT_STATIC_ASSERT --- srtcore/core.cpp | 4 +--- srtcore/sync_posix.cpp | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d73acfc26..cb71cfb6d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2027,9 +2027,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t return SRT_CMD_REJECT; } -#if HAVE_CXX11 - static_assert(SRT_HS_E_SIZE == SRT_HS_LATENCY + 1, "Assuming latency is the last field"); -#endif + SRT_STATIC_ASSERT(SRT_HS_E_SIZE == SRT_HS_LATENCY + 1, "Assuming latency is the last field"); if (bytelen < (SRT_HS_E_SIZE * sizeof(uint32_t))) { // Handshake extension message includes VERSION, FLAGS and LATENCY diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 56a89d6b9..997cf534c 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -77,7 +77,7 @@ void rdtsc(uint64_t& x) #endif } -int64_t get_cpu_frequency() +static int64_t get_cpu_frequency() { int64_t frequency = 1; // 1 tick per microsecond. @@ -123,9 +123,9 @@ static int count_subsecond_precision(int64_t ticks_per_us) return signs; } -const int64_t s_clock_ticks_per_us = get_cpu_frequency(); +static const int64_t s_clock_ticks_per_us = get_cpu_frequency(); -const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); +static const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); int clockSubsecondPrecision() { return s_clock_subsecond_precision; } From 4f06c2ec6f1142f34406aa4d023daa126b1fc89e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 14 Apr 2021 17:49:00 +0200 Subject: [PATCH 138/790] [core] Fixed srt_getsockopt cast for bool options (#1925) Cast to bool instead of int in srt_getsockopt(..) for: - SRTO_SENDER - SRTO_TSBPDMODE - SRTO_DRIFTTRACER - SRTO_ENFORCEDENCRYPTION Additionally: - Fixed TestEnforcedEncryption: bool optval - Minor improvements to Travis run_codecov --- .travis.yml | 2 +- srtcore/core.cpp | 16 ++++++++-------- test/test_enforced_encryption.cpp | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2e2205c0..334677592 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,9 +88,9 @@ script: fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; - source ./scripts/collect-gcov.sh; fi - if (( "$RUN_CODECOV" )); then + source ./scripts/collect-gcov.sh; bash <(curl -s https://codecov.io/bash); fi - if (( "$RUN_SONARCUBE" )); then diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cb71cfb6d..2dfdc9271 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -582,13 +582,13 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SENDER: - *(int32_t *)optval = m_config.bDataSender; - optlen = sizeof(int32_t); + *(bool *)optval = m_config.bDataSender; + optlen = sizeof(bool); break; case SRTO_TSBPDMODE: - *(int32_t *)optval = m_config.bTSBPD; - optlen = sizeof(int32_t); + *(bool *)optval = m_config.bTSBPD; + optlen = sizeof(bool); break; case SRTO_LATENCY: @@ -679,8 +679,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_DRIFTTRACER: - *(int*)optval = m_config.bDriftTracer; - optlen = sizeof(int); + *(bool*)optval = m_config.bDriftTracer; + optlen = sizeof(bool); break; case SRTO_MINVERSION: @@ -734,8 +734,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) #endif case SRTO_ENFORCEDENCRYPTION: - optlen = sizeof(int32_t); // also with TSBPDMODE and SENDER - *(int32_t *)optval = m_config.bEnforcedEnc; + optlen = sizeof(bool); + *(bool *)optval = m_config.bEnforcedEnc; break; case SRTO_IPV6ONLY: diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index cdb0ac719..48cb55f55 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -277,10 +277,10 @@ class TestEnforcedEncryption bool GetEnforcedEncryption(PEER_TYPE peer_type) { const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; - int value = -1; - int value_len = sizeof value; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&value, &value_len), SRT_SUCCESS); - return value ? true : false; + bool optval; + int optlen = sizeof optval; + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS); + return optval ? true : false; } @@ -503,8 +503,8 @@ class TestEnforcedEncryption int m_pollid = 0; - const int s_yes = 1; - const int s_no = 0; + const bool s_yes = true; + const bool s_no = false; const bool m_is_tracing = false; static const char* m_km_state[]; From 8c8d6fb083bf66bb8a6c96fde756f5920656a55d Mon Sep 17 00:00:00 2001 From: quink-black Date: Thu, 15 Apr 2021 15:26:32 +0800 Subject: [PATCH 139/790] [core] Fix build with mbedtls older than 428cc52a73. (#1945) Ref. https://github.com/ARMmbed/mbedtls/issues/1215 --- haicrypt/cryspr-mbedtls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haicrypt/cryspr-mbedtls.h b/haicrypt/cryspr-mbedtls.h index aa10da874..9cc5c6e44 100644 --- a/haicrypt/cryspr-mbedtls.h +++ b/haicrypt/cryspr-mbedtls.h @@ -55,7 +55,7 @@ This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK It is set from hte keystring through CRYSPR_methods.aes_set_key and passed to CRYSPR_methods.aes_XXX. */ -typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ +typedef mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ struct tag_CRYSPR_methods *crysprMbedtls(void); From f0533946a9b8cabeaa8655ef3df0b45536df8c05 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 14 Apr 2021 12:18:21 +0200 Subject: [PATCH 140/790] [core] SRTO_CONGESTION: Check optlen in getsockopt --- srtcore/core.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2dfdc9271..09e8a2da5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -697,6 +697,9 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_CONGESTION: + if (size_t(optlen) < m_config.sCongestion.size() + 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + strcpy((char *)optval, m_config.sCongestion.c_str()); optlen = (int) m_config.sCongestion.size(); break; From aa51e2d1a7ae124b336534658de52bbd3b836d90 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 10:02:42 +0200 Subject: [PATCH 141/790] [core] Apply PRE sockopt restriction in listening state (#1939) * Docs: clarified the description of sockopt restrictions. * Fixed FEC unit tests: call srt_setsockopt before srt_listen --- docs/API/API-socket-options.md | 11 ++--- srtcore/core.cpp | 2 +- test/test_fec_rebuilding.cpp | 78 +++++++++++++++++++--------------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index c1d4420e1..6c274666a 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -122,13 +122,14 @@ of SRT ever created and put into use. 2. **Restrict**: Defines restrictions on setting the option. The field is empty if the option is not settable (see **Dir** column): - - `pre-bind`: The option cannot be altered on a socket that is already bound (by calling + - `pre-bind`: The option cannot be altered on a socket that is already bound (by calling `srt_bind()` or any other function doing this, including automatic binding when trying to -connect, as well as accepted sockets). +connect, as well as accepted sockets). In other words, once an SRT socket has transitioned from +`SRTS_INIT` to `SRTS_OPENED` socket state. - - `pre`: Like pre-bind, but only for a connected socket (including an accepted socket). If -an option was set on a listener socket, it will be set the same on a socket returned by -`srt_accept()`. + - `pre`: The option cannot be altered on a socket that is in `SRTS_LISTENING`, `SRTS_CONNECTING` +or `SRTS_CONNECTED` state. If an option was set on a listener socket, it will be inherited +by a socket returned by `srt_accept()` (except for `SRTO_STREAMID`). - `post`: The option is unrestricted and can be altered at any time, including when the socket is connected, as well as on an accepted socket. The setting of this flag on a listening diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 09e8a2da5..82bee7857 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -359,7 +359,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) if (IsSet(oflags, SRTO_R_PREBIND) && m_bOpened) throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting)) + if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting || m_bListening)) throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); // Option execution. If this returns -1, there's no such option. diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index e289b384b..913473af7 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -287,14 +287,15 @@ TEST(TestFEC, Connection) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,cols:10,arq:never"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,cols:10,arq:never"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + srt_listen(l, 1); auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); @@ -339,15 +340,16 @@ TEST(TestFEC, ConnectionReorder) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,rows:10,cols:10"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,rows:10,cols:10"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -391,15 +393,16 @@ TEST(TestFEC, ConnectionFull1) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even"; - char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never"; - char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even"; + const char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even"; + const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never"; + const char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -442,15 +445,16 @@ TEST(TestFEC, ConnectionFull2) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even"; - char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always"; - char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even"; + const char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even"; + const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always"; + const char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -494,15 +498,16 @@ TEST(TestFEC, ConnectionMess) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:,cols:10"; - char fec_config2 [] = "fec,cols:,rows:10"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + const char fec_config1 [] = "fec,cols:,cols:10"; + const char fec_config2 [] = "fec,cols:,rows:10"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -546,13 +551,14 @@ TEST(TestFEC, ConnectionForced) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,rows:20,cols:20"; - char fec_config_final [] = "fec,cols:20,rows:20"; + const char fec_config1 [] = "fec,rows:20,cols:20"; + const char fec_config_final [] = "fec,cols:20,rows:20"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -592,14 +598,15 @@ TEST(TestFEC, RejectionConflict) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,cols:20,arq:never"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,cols:20,arq:never"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -634,12 +641,12 @@ TEST(TestFEC, RejectionIncompleteEmpty) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - - char fec_config1 [] = "fec,rows:10"; + const char fec_config1 [] = "fec,rows:10"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -675,14 +682,15 @@ TEST(TestFEC, RejectionIncomplete) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,rows:10"; - char fec_config2 [] = "fec,arq:never"; + const char fec_config1 [] = "fec,rows:10"; + const char fec_config2 [] = "fec,arq:never"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -740,7 +748,7 @@ TEST_F(TestFECRebuilding, NoRebuild) SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); // Use the sequence number of the last packet, as usual. - bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); + const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); ASSERT_EQ(have_fec_ctl, true); // By having all packets and FEC CTL packet, now stuff in @@ -817,7 +825,7 @@ TEST_F(TestFECRebuilding, Rebuild) SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); // Use the sequence number of the last packet, as usual. - bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); + const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); ASSERT_EQ(have_fec_ctl, true); // By having all packets and FEC CTL packet, now stuff in @@ -863,7 +871,7 @@ TEST_F(TestFECRebuilding, Rebuild) // And now receive the FEC control packet - bool want_passthru_fec = fec->receive(*fecpkt, loss); + const bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up EXPECT_EQ(loss.size(), 0); From b9d568e05705c38b6a76a088a04f8453bdf56cc6 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 15 Apr 2021 10:43:38 +0200 Subject: [PATCH 142/790] [core] Added more logs around accept errors (#1883) --- srtcore/api.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 3b8f81ef1..ec60e2389 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1067,16 +1067,25 @@ SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64 SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) { if (pw_addr && !pw_addrlen) + { + LOGC(cnlog.Error, log << "srt_accept: provided address, but address length parameter is missing"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } CUDTSocket* ls = locateSocket(listen); if (ls == NULL) + { + LOGC(cnlog.Error, log << "srt_accept: invalid listener socket ID value: " << listen); throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + } // the "listen" socket must be in LISTENING status if (ls->m_Status != SRTS_LISTENING) + { + LOGC(cnlog.Error, log << "srt_accept: socket @" << listen << " is not in listening state (forgot srt_listen?)"); throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); + } // no "accept" in rendezvous connection setup if (ls->m_pUDT->m_config.bRendezvous) @@ -1125,15 +1134,22 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ { // non-blocking receiving, no connection available if (!ls->m_pUDT->m_config.bSynRecving) + { + LOGC(cnlog.Error, log << "srt_accept: no pending connection available at the moment"); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + LOGC(cnlog.Error, log << "srt_accept: listener socket @" << listen << " is already closed"); // listening socket is closed throw CUDTException(MJ_SETUP, MN_CLOSED, 0); } CUDTSocket* s = locateSocket(u); if (s == NULL) + { + LOGC(cnlog.Error, log << "srt_accept: pending connection has unexpectedly closed"); throw CUDTException(MJ_SETUP, MN_CLOSED, 0); + } // Set properly the SRTO_GROUPCONNECT flag s->core().m_config.iGroupConnect = 0; @@ -1166,7 +1182,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ #endif ScopedLock cg(s->m_ControlLock); - + if (pw_addr != NULL && pw_addrlen != NULL) { // Check if the length of the buffer to fill the name in From 944287050f58a7901d485c21f75bf636742f3fdd Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 10:36:23 +0200 Subject: [PATCH 143/790] [core] Fixed getting SRTO_TLPKTDROP: return config value until connected. --- srtcore/core.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 82bee7857..555cc85df 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -610,7 +610,11 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_TLPKTDROP: - *(bool *)optval = m_bTLPktDrop; + if (m_bConnected) + *(bool *)optval = m_bTLPktDrop; + else + *(bool *)optval = m_config.bTLPktDrop; + optlen = sizeof(bool); break; From 35fb87ffffd8a60918644488c122cd9bf41730f1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 18:31:22 +0200 Subject: [PATCH 144/790] [core] const SrtOptionAction (#1942) --- srtcore/core.cpp | 21 ++++++++++++++------- srtcore/sync_posix.cpp | 6 +++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 555cc85df..9b93fab79 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -130,11 +130,15 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_LOSSMAXTTL }; -static const int32_t +const int32_t SRTO_R_PREBIND = BIT(0), //< cannot be modified after srt_bind() SRTO_R_PRE = BIT(1), //< cannot be modified after connection is established SRTO_POST_SPEC = BIT(2); //< executes some action after setting the option + +namespace srt +{ + struct SrtOptionAction { int flags[SRTO_E_SIZE]; @@ -210,8 +214,11 @@ struct SrtOptionAction // passed to a setting function. private_default[SRTO_STREAMID] = string(); } -} -srt_options_action; +}; + +const SrtOptionAction s_sockopt_action; + +} // namespace srt void CUDT::construct() @@ -297,9 +304,9 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) m_config = ancestor.m_config; // Reset values that shall not be derived to default ones. // These declarations should be consistent with SRTO_R_PRIVATE flag. - for (size_t i = 0; i < Size(srt_options_action.flags); ++i) + for (size_t i = 0; i < Size(s_sockopt_action.flags); ++i) { - string* pdef = map_getp(srt_options_action.private_default, SRT_SOCKOPT(i)); + const string* pdef = map_getp(s_sockopt_action.private_default, SRT_SOCKOPT(i)); if (pdef) { try @@ -342,12 +349,12 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - // Match check (confirm optName as index for srt_options_action) + // Match check (confirm optName as index for s_sockopt_action) if (int(optName) < 0 || int(optName) >= int(SRTO_E_SIZE)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Restriction check - const int oflags = srt_options_action.flags[optName]; + const int oflags = s_sockopt_action.flags[optName]; ScopedLock cg (m_ConnectionLock); ScopedLock sendguard (m_SendLock); diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 997cf534c..0972dd731 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -38,7 +38,7 @@ namespace srt namespace sync { -void rdtsc(uint64_t& x) +static void rdtsc(uint64_t& x) { #if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA32_RDTSC uint32_t lval, hval; @@ -123,9 +123,9 @@ static int count_subsecond_precision(int64_t ticks_per_us) return signs; } -static const int64_t s_clock_ticks_per_us = get_cpu_frequency(); +const int64_t s_clock_ticks_per_us = get_cpu_frequency(); -static const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); +const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); int clockSubsecondPrecision() { return s_clock_subsecond_precision; } From 07320ab6f7f3a2166d95a8cab71435f2c9eaddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Apr 2021 10:02:22 +0200 Subject: [PATCH 145/790] [apps] Option utility fix: handle properly single dash as option value --- apps/apputil.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0afe40cd5..f772beadf 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -205,7 +205,10 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector isoption = true; // If a[0] isn't NUL - because it is dash - then // we can safely check a[1]. - if (a[1] && isdigit(a[1])) + // An expression starting with a dash is not + // an option marker if it is a single dash or + // a negative number. + if (!a[1] || isdigit(a[1])) isoption = false; } From 97efa1b959ce1623f4932fac06d4b94cd8adb3a7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 16 Apr 2021 14:55:22 +0200 Subject: [PATCH 146/790] [docs] Fixed restrictionf for SRTO_MSS and SRTO_LOSSMAXTTL (#1954) - SRTO_MSS: 'pre-bind' restriction instead of pre - SRTO_LOSSMAXTTL has 'post' restriction --- docs/API/API-socket-options.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 6c274666a..4d1565c5c 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -216,12 +216,12 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | | [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | | [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [`SRTO_MSS`](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | | [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | @@ -733,7 +733,7 @@ Linger time on close (see [SO\_LINGER](http://man7.org/linux/man-pages/man7/sock | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_LOSSMAXTTL` | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| `SRTO_LOSSMAXTTL` | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | The value up to which the *Reorder Tolerance* may grow. The *Reorder Tolerance* is the number of packets that must follow the experienced "gap" in sequence numbers @@ -833,7 +833,7 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | Maximum Segment Size. Used for buffer allocation and rate calculation using packet counter assuming fully filled packets. Each party can set its own MSS From 8608ad26ecb095bfb5159530e7f15334c7771a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Apr 2021 13:07:14 +0200 Subject: [PATCH 147/790] [core] Removed logging in cleanup --- srtcore/api.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ec60e2389..71e4a8772 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -294,6 +294,14 @@ int CUDTUnited::startup() int CUDTUnited::cleanup() { + // IMPORTANT!!! + // In this function there must be NO LOGGING AT ALL. This function may + // potentially be called from within the global program destructor, and + // therefore some of the facilities used by the logging system - including + // the default std::cerr object bound to it by default, but also a different + // stream that the user's app has bound to it, and which got destroyed + // together with already exited main() - may be already deleted when + // executing this procedure. ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) @@ -303,7 +311,6 @@ int CUDTUnited::cleanup() return 0; m_bClosing = true; - HLOGC(inlog.Debug, log << "GarbageCollector: thread EXIT"); // NOTE: we can do relaxed signaling here because // waiting on m_GCStopCond has a 1-second timeout, // after which the m_bClosing flag is cheched, which From 43b78eb6c3a4efe0ba49e808dadc50775dc67918 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 16 Apr 2021 16:18:41 +0200 Subject: [PATCH 148/790] [docs] API-functions: pass optlen in srt_getsockopt (#1941) Further corrections, including C string handling. --- docs/API/API-functions.md | 51 +++++++++++++++++++--------------- docs/API/API-socket-options.md | 31 +++++++++++---------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 0e65d5ab3..cf800413f 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1558,24 +1558,29 @@ port number after it has been autoselected. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- - + ### srt_getsockopt ### srt_getsockflag -``` + +```c++ int srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); ``` -Gets the value of the given socket option (from a socket or a group). +Gets the value of the given socket option (from a socket or a group). -The first version ([`srt_getsockopt`](#srt_getsockopt)) respects the BSD socket -API convention, although the "level" parameter is ignored. The second version +The first version ([`srt_getsockopt`](#srt_getsockopt)) follows the BSD socket +API convention, although the "level" parameter is ignored. The second version ([`srt_getsockflag`](#srt_getsockflag)) omits the "level" parameter completely. -Options correspond to various data types, so you need to know what data type is -assigned to a particular option, and to pass a variable of the appropriate data -type. Specifications are provided in the `apps/socketoptions.hpp` file at the -`srt_options` object declaration. +Options correspond to various data types (see [API-socket-options.md](./API-socket-options.md)). +A variable `optval` of the appropriate data type has to be passed. +The integer value of `optlen` should originally contain the size of the `optval` type provided; +on return, it will be set to the size of the value returned. +For most options, it will be the size of an integer. Some options, however, use types `bool`, `int64_t`, `C string`, etc. +(see [API-socket-options.md](./API-socket-options.md#sockopt_types)). + +The application is responsible for allocating sufficient memory space as defined and pointed to by `optval`. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -1586,7 +1591,7 @@ type. Specifications are provided in the `apps/socketoptions.hpp` file at the |:-------------------------------- |:---------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | | [`SRT_EINVOP`](#srt_einvop) | Option `opt` indicates no valid option | -| | | +| | | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1595,19 +1600,19 @@ type. Specifications are provided in the `apps/socketoptions.hpp` file at the ### srt_setsockopt ### srt_setsockflag -``` +```c++ int srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); ``` -Sets a value for a socket option in the socket or group. +Sets a value for a socket option in the socket or group. -The first version ([`srt_setsockopt`](#srt_setsockopt)) respects the BSD socket -API convention, although the "level" parameter is ignored. The second version +The first version ([`srt_setsockopt`](#srt_setsockopt)) follows the BSD socket +API convention, although the "level" parameter is ignored. The second version ([`srt_setsockflag`](#srt_setsockflag)) omits the "level" parameter completely. -Options correspond to various data types, so you need to know what data type is -assigned to a particular option, and to pass a variable of the appropriate data +Options correspond to various data types, so you need to know what data type is +assigned to a particular option, and to pass a variable of the appropriate data type with the option value to be set. Please note that some of the options can only be set on sockets or only on @@ -1619,14 +1624,16 @@ are then derived by the member sockets. | `SRT_ERROR` | (-1) in case of error, otherwise 0 | | | | -| Errors | | -|:------------------------------- |:--------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | -| [`SRT_EINVOP`](#srt_einvop) | Option `opt` indicates no valid option | +| Errors | | +|:----------------------------------- |:--------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EINVPARAM`](#srt_einvparam) | Option `opt` indicates no valid option | +| [`SRT_EBOUNDSOCK`](#srt_eboundsock) | Tried to set an option with PRE_BIND restriction on a bound socket. | +| [`SRT_ECONNSOCK`](#srt_econnsock) | Tried to set an option with PRE_BIND or PRE restriction on a socket in connecting/listening/connected state. | | | | -**NOTE*: Various other errors may result from problems when setting a -specific option (see option description for details). +**NOTE*: Various other errors may result from problems when setting a +specific option (see option description in [API-socket-options.md](./API-socket-options.md) for details). [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 4d1565c5c..9a3342f7b 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -7,31 +7,32 @@ to the system `setsockopt/getsockopt` functions. - [Getting and Setting Options](#getting-and-setting-options) - [List of Options](#list-of-options) -## Types Used in Socket Options +## Types Used in Socket Options Possible types of socket options are: -- `int32_t` - This type can usually be treated as an `int` equivalent since it -does not change size on 64-bit systems. For clarity, options use this fixed size -integer. In some cases the value is expressed using an enumeration type (see below). +- `int32_t` - a 32-bit integer. On most systems similar to `int`. +In some cases the value is expressed using an enumeration type +(see [Enumeration types...](#enumeration_types) section below). -- `int64_t` - Some options need the parameter specified as 64-bit integer. +- `int64_t` - a 64-bit integer. -- `bool` - Requires the use of a boolean type (`` for C, or built-in +- `bool` - a Boolean type (`` for C, or built-in for C++). When *setting* an option, passing the value through an `int` type is -also properly recognized. When *getting* an option, however, you should use the -`bool` type, although you can risk passing a variable of `int` type initialized -with 0 and then checking if the resulting value is equal to 0 (just don't compare -the result with 1). +also properly recognized. When *getting* an option, however, the`bool` type +should be used. It is also possible to pass a variable of `int` type initialized +with 0 and then comparing the resulting value with 0 (just don't compare +the result with 1 or `true`). -- `string` - When *setting* an option, pass the character array pointer as value -and the string length as length. When *getting*, pass an array of sufficient size -(as specified in the size variable). Every option with this type that can be -read should specify the maximum length of that array. +- `string` - a C string. When *setting* an option, a `const char*` character array pointer +is expected to be passed in `optval` and the array length in `optlen` **without the terminating NULL character**. +When *getting*, an array is expected to be passed in `optval` with a +sufficient size **with an extra space for the terminating NULL character** provided in `optlen`. +The return value of `optlen` does not count the terminating NULL character. - `linger` - Linger structure. Used exclusively with `SRTO_LINGER`. -### Enumeration Types Used in Options +### Enumeration Types Used in Options #### `SRT_TRANSTYPE` From 3bf5ceb30261f8b5b1676a8972068659f549fc4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 19 Apr 2021 10:48:10 +0200 Subject: [PATCH 149/790] [core] Check invalid sockopt values (#1956) * SRTO_CONNTIMEO: reject negative * SRTO_PAYLOADSIZE: reject negative and fix FEC * SRTO_SNDDROPDELAY >= -1 * SRTO_SNDTIMEO >= -1 * SRTO_*LATENCY >= 0 * SRTO_PEERIDLETIMEO >= 0 * SRTO_RCVTIMEO >= -1 --- docs/API/API-socket-options.md | 34 +++++++++--------- srtcore/socketconfig.h | 63 ++++++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 9a3342f7b..7f0316a6e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -198,7 +198,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | | :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :---------------: | :------: |:---:|:-----:| | [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | +| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | \* | W | S | | [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | | [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | | [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | @@ -212,49 +212,49 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | | [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | | [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | +| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. \* | RW | GSD | | [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | | [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 \* | 0.. | RW | GSD | | [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | | [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | \* | RW | GSD | | [`SRTO_MSS`](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | \* | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | | [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | | [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | +| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | \* | RW | GSD | | [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | | [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | | [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | | [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | | [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | +| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | \* | 0.. | RW | GSD | | [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | | [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | | [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | | [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | | [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | | [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | +| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | \* | -1.. | W | GSD+ | | [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | | [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | | [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | | [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | \* | | RW | GSD | +| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | \* | W | S | +| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | \* | | W | S | +| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | +| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | \* | RW | GSD+ | | [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions @@ -997,7 +997,7 @@ encrypted connection, they have to simply set the same passphrase. | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | Sets the maximum declared size of a single call to sending function in Live mode. When set to 0, there's no limit for a single sending call. diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 969d624e5..c63a41abb 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -502,7 +502,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iSndTimeOut = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndTimeOut = val; } }; @@ -511,7 +515,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvTimeOut = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvTimeOut = val; } }; @@ -660,8 +668,12 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvLatency = cast_optval(optval, optlen); - co.iPeerLatency = cast_optval(optval); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + co.iPeerLatency = val; } }; template<> @@ -669,7 +681,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvLatency = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; } }; template<> @@ -677,7 +693,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iPeerLatency = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerLatency = val; } }; template<> @@ -693,9 +713,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - // Surprise: you may be connected to alter this option. - // The application may manipulate this option on sender while transmitting. - co.iSndDropDelay = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndDropDelay = val; } }; template<> @@ -803,8 +825,12 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + using namespace srt::sync; - co.tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); + co.tdConnTimeOut = milliseconds_from(val); } }; @@ -894,8 +920,13 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { using namespace srt_logging; + const int val = cast_optval(optval, optlen); + if (val < 0) + { + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } - if (*(int *)optval > SRT_LIVE_MAX_PLSIZE) + if (val > SRT_LIVE_MAX_PLSIZE) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -915,7 +946,7 @@ struct CSrtConfigSetter } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.zExpPayloadSize > efc_max_payload_size) + if (val > efc_max_payload_size) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size @@ -924,7 +955,7 @@ struct CSrtConfigSetter } } - co.zExpPayloadSize = cast_optval(optval, optlen); + co.zExpPayloadSize = val; } }; @@ -1072,7 +1103,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iPeerIdleTimeout = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerIdleTimeout = val; } }; From 90c62af4e51b1098bbfd06cee8c362bdcbb80431 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Apr 2021 17:12:25 +0200 Subject: [PATCH 150/790] [core] Fixed type conversion build warnings --- apps/srt-tunnel.cpp | 4 ++-- srtcore/buffer.cpp | 4 ++-- srtcore/channel.cpp | 6 +++++- srtcore/core.cpp | 18 +++++++++--------- srtcore/core.h | 16 ++++------------ srtcore/group.cpp | 14 +++++++------- srtcore/logging.h | 3 +++ srtcore/socketconfig.h | 6 +++--- srtcore/window.h | 2 +- 9 files changed, 36 insertions(+), 37 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 0567b5764..e5175ce8b 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -701,10 +701,10 @@ unique_ptr TcpMedium::Accept() // Configure 1s timeout timeval timeout_1s { 1, 0 }; - int st = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s); + int st SRT_ATR_UNUSED = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s); timeval re; socklen_t size = sizeof re; - int st2 = getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&re, &size); + int st2 SRT_ATR_UNUSED = getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&re, &size); LOGP(applog.Debug, "Setting SO_RCVTIMEO to @", m_socket, ": ", st == -1 ? "FAILED" : "SUCCEEDED", ", read-back value: ", st2 == -1 ? int64_t(-1) : (int64_t(re.tv_sec)*1000000 + re.tv_usec)/1000, "ms"); diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 5ca443485..20f5da3d8 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1768,8 +1768,8 @@ steady_clock::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t timestamp_us) { carryover = int64_t(CPacket::MAX_TIMESTAMP) + 1; } - // - else if ((timestamp_us >= TSBPD_WRAP_PERIOD) && (timestamp_us <= (TSBPD_WRAP_PERIOD * 2))) + // timestamp_us >= TSBPD_WRAP_PERIOD + else if (timestamp_us <= (TSBPD_WRAP_PERIOD * 2)) { /* Exiting wrap check period (if for packet delivery head) */ m_bTsbPdWrapCheck = false; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index c35510747..865eaaabc 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -183,8 +183,9 @@ void CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, + const int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); +#if ENABLE_LOGGING if (res == -1) { int err = errno; @@ -192,6 +193,7 @@ void CChannel::createSocket(int family) LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " << m_mcfg.iIpV6Only << ": " << SysStrError(err, msg, 159)); } +#endif // ENABLE_LOGGING } } @@ -352,9 +354,11 @@ void CChannel::setUDPSockOpt() if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) { +#if ENABLE_LOGGING char buf[255]; const char* err = SysStrError(NET_ERROR, buf, 255); LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); +#endif // ENABLE_LOGGING throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9b93fab79..cefb716c5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -313,7 +313,7 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) { // Ignore errors here - this is a development-time granted // value, not user-provided value. - m_config.set(SRT_SOCKOPT(i), pdef->data(), pdef->size()); + m_config.set(SRT_SOCKOPT(i), pdef->data(), (int) pdef->size()); } catch (...) { @@ -6567,7 +6567,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING - double bw = Bps2Mbps( m_iBandwidth * m_iMaxSRTPayloadSize ); + double bw = Bps2Mbps(int64_t(m_iBandwidth) * m_iMaxSRTPayloadSize ); HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)"); #endif } @@ -7024,10 +7024,10 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; - double interval = count_microseconds(currtime - m_stats.tsLastSampleTime); + const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; - perf->usPktSndPeriod = count_microseconds(m_tdSendInterval); + perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval); perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); @@ -7172,9 +7172,9 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling // Bytes/s - int bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW - m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW - 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling + const int64_t bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW + m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW + 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling // Note: setting bw == 0 uses BW_INFINITE value in LiveCC m_CongCtl->updateBandwidth(m_config.llMaxBW, bw); @@ -9408,12 +9408,12 @@ int CUDT::processData(CUnit* in_unit) { IF_HEAVY_LOGGING(exc_type = "BELATED"); steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - long bltime = CountIIR( + const double bltime = (double) CountIIR( uint64_t(m_stats.traceBelatedTime) * 1000, count_microseconds(steady_clock::now() - tsbpdtime), 0.2); enterCS(m_StatsLock); - m_stats.traceBelatedTime = double(bltime) / 1000.0; + m_stats.traceBelatedTime = bltime / 1000.0; m_stats.traceRcvBelated++; leaveCS(m_StatsLock); HLOGC(qrlog.Debug, diff --git a/srtcore/core.h b/srtcore/core.h index 70dbb2f7d..b3498c814 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -364,7 +364,7 @@ class CUDT int minSndSize(int len = 0) const { - const int ps = maxPayloadSize(); + const int ps = (int) maxPayloadSize(); if (len == 0) // wierd, can't use non-static data member as default argument! len = ps; return m_config.bMessageAPI ? (len+ps-1)/ps : 1; @@ -379,7 +379,7 @@ class CUDT // So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1) // XXX Would be nice to check if local_time > m_tsStartTime, // otherwise it may go unnoticed with clock skew. - return srt::sync::count_microseconds(from_time - m_stats.tsStartTime); + return (int32_t) srt::sync::count_microseconds(from_time - m_stats.tsStartTime); } void setPacketTS(CPacket& p, const time_point& local_time) @@ -400,18 +400,10 @@ class CUDT { using namespace srt::sync; // Random Initial Sequence Number (normal mode) - srand(count_microseconds(steady_clock::now().time_since_epoch())); + srand((unsigned) count_microseconds(steady_clock::now().time_since_epoch())); return (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); } - // XXX See CUDT::tsbpd() to see how to implement it. This should - // do the same as TLPKTDROP feature when skipping packets that are agreed - // to be lost. Note that this is predicted to be called with TSBPD off. - // This is to be exposed for the application so that it can require this - // sequence to be skipped, if that packet has been otherwise arrived through - // a different channel. - void skipIncoming(int32_t seq); - // For SRT_tsbpdLoop static CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop std::set& pollset() { return m_sPollID; } @@ -669,7 +661,7 @@ class CUDT int sndSpaceLeft() { - return sndBuffersLeft() * maxPayloadSize(); + return static_cast(sndBuffersLeft() * maxPayloadSize()); } int sndBuffersLeft() diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 4c8feff31..032ac1580 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -147,8 +147,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // time when the connection process is done, until the first reading/writing happens. ScopedLock cg(m_GroupLock); - SRTSOCKET mpeer; - steady_clock::time_point start_time; + IF_LOGGING(SRTSOCKET mpeer); + IF_LOGGING(steady_clock::time_point start_time); bool found = false; @@ -159,8 +159,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // Found it. Get the socket's peer's ID and this socket's // Start Time. Once it's delivered, this can be used to calculate // the Master-to-Slave start time difference. - mpeer = gi->ps->m_PeerID; - start_time = gi->ps->core().socketStartTime(); + IF_LOGGING(mpeer = gi->ps->m_PeerID); + IF_LOGGING(start_time = gi->ps->core().socketStartTime()); HLOGC(gmlog.Debug, log << "getMasterData: found RUNNING master @" << gi->id << " - reporting master's peer $" << mpeer << " starting at " << FormatTime(start_time)); @@ -185,8 +185,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // Found it. Get the socket's peer's ID and this socket's // Start Time. Once it's delivered, this can be used to calculate // the Master-to-Slave start time difference. - mpeer = gi->ps->core().m_PeerID; - start_time = gi->ps->core().socketStartTime(); + IF_LOGGING(mpeer = gi->ps->core().m_PeerID); + IF_LOGGING(start_time = gi->ps->core().socketStartTime()); HLOGC(gmlog.Debug, log << "getMasterData: found IDLE/PENDING master @" << gi->id << " - reporting master's peer $" << mpeer << " starting at " << FormatTime(start_time)); @@ -203,7 +203,7 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) { // The returned master_st is the master's start time. Calculate the // differene time. - steady_clock::duration master_tdiff = m_tsStartTime - start_time; + IF_LOGGING(steady_clock::duration master_tdiff = m_tsStartTime - start_time); LOGC(cnlog.Debug, log << CONID() << "FOUND GROUP MASTER LINK: peer=$" << mpeer << " - start time diff: " << FormatDuration(master_tdiff)); } diff --git a/srtcore/logging.h b/srtcore/logging.h index bfdfb375e..cfb239f1e 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -66,6 +66,8 @@ written by // Usage: LOGP(gglog.Debug, param1, param2, param3); #define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__) +#define IF_LOGGING(instr) instr + #if ENABLE_HEAVY_LOGGING #define HLOGC LOGC @@ -95,6 +97,7 @@ written by #define HLOGP(...) #define IF_HEAVY_LOGGING(instr) (void)0 +#define IF_LOGGING(instr) (void)0 #endif diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index c63a41abb..6b8a9325e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -146,7 +146,7 @@ class StringStorage memcpy(stor, s, length); stor[length] = 0; - len = length; + len = (int) length; return true; } @@ -945,8 +945,8 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (val > efc_max_payload_size) + const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (size_t(val) > efc_max_payload_size) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size diff --git a/srtcore/window.h b/srtcore/window.h index b7f510163..7dbfb73bf 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -218,7 +218,7 @@ class CPktTimeWindow: CPktTimeWindowTools m_tsCurrArrTime = srt::sync::steady_clock::now(); // record the packet interval between the current and the last one - m_aPktWindow[m_iPktWindowPtr] = srt::sync::count_microseconds(m_tsCurrArrTime - m_tsLastArrTime); + m_aPktWindow[m_iPktWindowPtr] = (int) srt::sync::count_microseconds(m_tsCurrArrTime - m_tsLastArrTime); m_aBytesWindow[m_iPktWindowPtr] = pktsz; // the window is logically circular From a95e5aee722e64ddc0f00762aaca0721728bbd13 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Apr 2021 18:04:55 +0200 Subject: [PATCH 151/790] [build] Added -Werror to Travis CI Linux and MacOS jobs --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 334677592..4ffe49add 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,20 +23,24 @@ matrix: - os: linux env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_EXPERIMENTAL_BONDING=ON' + - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_EXPERIMENTAL_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - RUN_SONARCUBE=1 - RUN_CODECOV=1 - env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_LOGGING=OFF -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_EXPERIMENTAL_BONDING=ON' + - BUILD_OPTS='-DENABLE_LOGGING=OFF -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_EXPERIMENTAL_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - os: linux env: BUILD_TYPE=Release - os: osx osx_image: xcode11.1 - env: BUILD_TYPE=Debug + env: + - BUILD_TYPE=Debug + - BUILD_OPTS='-DCMAKE_CXX_FLAGS="-Werror"' - os: osx osx_image: xcode11.1 - env: BUILD_TYPE=Release + env: + - BUILD_TYPE=Release + - BUILD_OPTS='-DCMAKE_CXX_FLAGS="-Werror"' - os: linux compiler: x86_64-w64-mingw32-g++ addons: From 672f0cbfe20efdcf671e356ea2e5d456aa79d09e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 23 Apr 2021 11:05:58 +0200 Subject: [PATCH 152/790] [apps] Fixed virtual call from destructor. Virtual functions should not be invoked from a constructor or destructor of the same class. --- apps/transmitmedia.hpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 604a8dfba..527f005d9 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -56,7 +56,7 @@ class SrtCommon SRTSOCKET Socket() const { return m_sock; } SRTSOCKET Listener() const { return m_bindsock; } - virtual void Close(); + void Close(); protected: @@ -109,7 +109,6 @@ class SrtSource: public Source, public SrtCommon bool IsOpen() override { return IsUsable(); } bool End() override { return IsBroken(); } - void Close() override { return SrtCommon::Close(); } SRTSOCKET GetSRTSocket() const override { @@ -137,7 +136,6 @@ class SrtTarget: public Target, public SrtCommon int Write(const char* data, size_t size, int64_t src_time, ostream &out_stats = cout) override; bool IsOpen() override { return IsUsable(); } bool Broken() override { return IsBroken(); } - void Close() override { return SrtCommon::Close(); } size_t Still() override { @@ -181,18 +179,8 @@ class SrtModel: public SrtCommon string m_host; int m_port = 0; - SrtModel(string host, int port, map par); void Establish(std::string& name); - - void Close() - { - if (m_sock != SRT_INVALID_SOCK) - { - srt_close(m_sock); - m_sock = SRT_INVALID_SOCK; - } - } }; From 5dbceb20055e0575b937073139665571f370a88b Mon Sep 17 00:00:00 2001 From: kkarsten Date: Mon, 26 Apr 2021 10:39:23 +0200 Subject: [PATCH 153/790] [docs] Update srt-live-transmit.md (#1967) A note on passphrase length --- docs/apps/srt-live-transmit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 184bb32a7..287a47649 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -314,7 +314,7 @@ All other parameters are SRT socket options. The following have the following va | `nakreport` | `bool` | `SRTO_NAKREPORT` | Enables/disables periodic NAK reports | | `oheadbw` | 5..100 | `SRTO_OHEADBW` | limits bandwidth overhead, percents | | `packetfilter` | `string` | `SRTO_PACKETFILTER` | Set up the packet filter. | -| `passphrase` | `string` | `SRTO_PASSPHRASE` | Password for the encrypted transmission. | +| `passphrase` | `string` | `SRTO_PASSPHRASE` | Password for the encrypted transmission. (must be 10 to 79 characters) | | `payloadsize` | 0.. | `SRTO_PAYLOADSIZE` | Maximum payload size. | | `pbkeylen` | {16, 24, 32} | `SRTO_PBKEYLEN` | Crypto key length in bytes. | | `peeridletimeo` | `ms` | `SRTO_PEERIDLETIMEO` | Peer idle timeout. | From 291e010fbf91b13b92e1cde5dcb824a9d7f4e353 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Apr 2021 10:44:11 +0200 Subject: [PATCH 154/790] [core] New API function srt_clock_type() (#1887) --- docs/API/API-functions.md | 30 ++++++++++++++++++++++++++++++ srtcore/srt.h | 12 ++++++++++++ srtcore/srt_c_api.cpp | 5 +++++ srtcore/sync.h | 10 ---------- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index cf800413f..44f8ac19a 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -135,6 +135,7 @@ |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | | [srt_time_now](#srt_time_now) | Get time in microseconds elapsed since epoch using SRT internal clock
(steady or monotonic clock) | | [srt_connection_time](#srt_connection_time) | Get connection time in microseconds elapsed since epoch using SRT internal clock
(steady or monotonic clock) | +| [srt_clock_type](#srt_clock_type) | Get the type of clock used internally by SRT | | | |

Diagnostics

@@ -2646,7 +2647,36 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistic --- +### srt_clock_type +```c +int srt_clock_type(void); +``` + +Get the type of clock used internally by SRT to be used only for informtational peurpose. +Using any time source except for [`srt_time_now()`](#srt_time_now) and [`srt_connection_time(SRTSOCKET)`](#srt_connection_time) +to timestamp packets submitted to SRT is not recommended and must be done with awareness and at your own risk. + +| Returns | Clock Type | Description | +| :------ | :---------------------------------- | :------------------------------------------| +| 0 | `SRT_SYNC_CLOCK_STDCXX_STEADY` | C++11 `std::chrono::steady_clock` | +| 1 | `SRT_SYNC_CLOCK_GETTIME_MONOTONIC` | `clock_gettime` with `CLOCK_MONOTONIC` | +| 2 | `SRT_SYNC_CLOCK_WINQPC` | Windows `QueryPerformanceCounter(..)` | +| 3 | `SRT_SYNC_CLOCK_MACH_ABSTIME` | `mach_absolute_time()` | +| 4 | `SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY` | POSIX `gettimeofday(..)` | +| 5 | `SRT_SYNC_CLOCK_AMD64_RDTSC` | `asm("rdtsc" ..)` | +| 6 | `SRT_SYNC_CLOCK_IA32_RDTSC` | `asm volatile("rdtsc" ..)` | +| 7 | `SRT_SYNC_CLOCK_IA64_ITC` | `asm("mov %0=ar.itc" ..)` | + +| Errors | | +|:--------------------------------- |:---------------------------------------------------------- | +| None | | +| | | + + +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- ## Diagnostics diff --git a/srtcore/srt.h b/srtcore/srt.h index f9fa24655..92be107d2 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -995,6 +995,18 @@ SRT_API int64_t srt_time_now(void); SRT_API int64_t srt_connection_time(SRTSOCKET sock); +// Possible internal clock types +#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock +#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC +#define SRT_SYNC_CLOCK_WINQPC 2 +#define SRT_SYNC_CLOCK_MACH_ABSTIME 3 +#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4 +#define SRT_SYNC_CLOCK_AMD64_RDTSC 5 +#define SRT_SYNC_CLOCK_IA32_RDTSC 6 +#define SRT_SYNC_CLOCK_IA64_ITC 7 + +SRT_API int srt_clock_type(void); + #ifdef __cplusplus } #endif diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 1763198ed..bfd130148 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -410,4 +410,9 @@ int64_t srt_connection_time(SRTSOCKET sock) return CUDT::socketStartTime(sock); } +int srt_clock_type() +{ + return SRT_SYNC_CLOCK; +} + } diff --git a/srtcore/sync.h b/srtcore/sync.h index 271b15426..7457c3df9 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -11,16 +11,6 @@ #ifndef INC_SRT_SYNC_H #define INC_SRT_SYNC_H -// Possible internal clock types -#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock -#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC -#define SRT_SYNC_CLOCK_WINQPC 2 -#define SRT_SYNC_CLOCK_MACH_ABSTIME 3 -#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4 -#define SRT_SYNC_CLOCK_AMD64_RDTSC 5 -#define SRT_SYNC_CLOCK_IA32_RDTSC 6 -#define SRT_SYNC_CLOCK_IA64_ITC 7 - #include #include #ifdef ENABLE_STDCXX_SYNC From d898f1ccee4ab53b00101c193d9fd7ea23304575 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:16:18 +0200 Subject: [PATCH 155/790] [core] Improved RTT estimation (#1957) * Receiver: use the first RTT estimation without smoothing. * Sender: take RTT value from ACK in case of unidirectional transmission. --- CMakeLists.txt | 1 + srtcore/core.cpp | 190 ++++++++++++++++++++++++++++++++++++++++++----- srtcore/core.h | 26 ++++--- 3 files changed, 189 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25f34c881..df43a1e26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ endforeach() # SRT_DEBUG_TLPKTDROP_DROPSEQ 1 # SRT_DEBUG_SNDQ_HIGHRATE 1 # SRT_DEBUG_BONDING_STATES 1 +# SRT_DEBUG_RTT 1 /* RTT trace */ # SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */ # option defaults diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cefb716c5..7f23e2653 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -241,7 +241,6 @@ void CUDT::construct() // Will be reset to 0 for HSv5, this value is important for HSv4. m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; - // Initial status m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -932,8 +931,10 @@ void CUDT::open() m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; m_pRNode->m_bOnList = false; - m_iRTT = 10 * COMM_SYN_INTERVAL_US; - m_iRTTVar = m_iRTT >> 1; + // Set initial values of smoothed RTT and RTT variance. + m_iRTT = INITIAL_RTT; + m_iRTTVar = INITIAL_RTTVAR; + m_bIsFirstRTTReceived = false; // set minimum NAK and EXP timeout to 300ms m_tdMinNakInterval = milliseconds_from(300); @@ -1867,6 +1868,74 @@ void SrtExtractHandshakeExtensions(const char* bufbegin, size_t buflength, } } +#if SRT_DEBUG_RTT +class RttTracer +{ +public: + RttTracer() + { + } + + ~RttTracer() + { + srt::sync::ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(const srt::sync::steady_clock::time_point& currtime, + const std::string& event, int rtt_sample, int rttvar_sample, + bool is_smoothed_rtt_reset, int64_t recvTotal, + int smoothed_rtt, int rttvar) + { + srt::sync::ScopedLock lck(m_mtx); + create_file(); + + m_fout << srt::sync::FormatTimeSys(currtime) << ","; + m_fout << srt::sync::FormatTime(currtime) << ","; + m_fout << event << ","; + m_fout << rtt_sample << ","; + m_fout << rttvar_sample << ","; + m_fout << is_smoothed_rtt_reset << ","; + m_fout << recvTotal << ","; + m_fout << smoothed_rtt << ","; + m_fout << rttvar << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "Timepoint_SYST,Timepoint_STDY,Event,usRTTSample," + "usRTTVarSample,IsSmoothedRTTReset,pktsRecvTotal," + "usSmoothedRTT,usRTTVar\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "rtt_trace_" + str_tnow + "_" + SRT_SYNC_CLOCK_STR + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; +}; + +RttTracer s_rtt_trace; +#endif + bool CUDT::processSrtMsg(const CPacket *ctrlpkt) { @@ -3261,8 +3330,8 @@ void CUDT::synchronizeWithGroup(CUDTGroup* gp) } } } - #endif + void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { ScopedLock cg (m_ConnectionLock); @@ -4485,10 +4554,15 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; - m_iRTTVar = m_iRTT >> 1; + m_iRTTVar = ib.m_iRTT / 2; m_iBandwidth = ib.m_iBandwidth; } +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Connect", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); +#endif + SRT_REJECT_REASON rr = setupCC(); if (rr != SRT_REJ_UNKNOWN) { @@ -5384,10 +5458,15 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; - m_iRTTVar = m_iRTT >> 1; + m_iRTTVar = ib.m_iRTT / 2; m_iBandwidth = ib.m_iBandwidth; } +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Accept", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); +#endif + m_PeerAddr = peer; // This should extract the HSREQ and KMREQ portion in the handshake packet. @@ -5851,6 +5930,11 @@ bool CUDT::closeInternal() ib.m_iBandwidth = m_iBandwidth; m_pCache->update(&ib); +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Cache", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, -1); +#endif + m_bConnected = false; } @@ -7998,15 +8082,63 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point } // This check covers fields up to ACKD_BUFFERLEFT. - // Update RTT - // m_iRTT = ackdata[ACKD_RTT]; - // m_iRTTVar = ackdata[ACKD_RTTVAR]; - // XXX These ^^^ commented-out were blocked in UDT; - // the current RTT calculations are exactly the same as in UDT4. - const int rtt = ackdata[ACKD_RTT]; + // Extract RTT estimate and RTTVar from the ACK packet. + const int rtt = ackdata[ACKD_RTT]; + const int rttvar = ackdata[ACKD_RTTVAR]; + + // Update the values of smoothed RTT and the variation in RTT samples + // on subsequent RTT estimates extracted from the ACK packets + // (during transmission). + if (m_bIsFirstRTTReceived) + { + // Suppose transmission is bidirectional if sender is also receiving + // data packets. + enterCS(m_StatsLock); + const bool bPktsReceived = m_stats.recvTotal != 0; + leaveCS(m_StatsLock); - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + if (bPktsReceived) // Transmission is bidirectional. + { + // RTT value extracted from the ACK packet (rtt) is already smoothed + // RTT obtained at the receiver side. Apply EWMA anyway for the second + // time on the sender side. Ignore initial values which might arrive + // after the smoothed RTT on the sender side has been + // reset to the very first RTT sample received from the receiver. + // TODO: The case of bidirectional transmission requires further + // improvements and testing. Double smoothing is applied here to be + // consistent with the previous behavior. + + if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + { + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + } + } + else // Transmission is unidirectional. + { + // Simply take the values of smoothed RTT and RTT variance from + // the ACK packet. + m_iRTT = rtt; + m_iRTTVar = rttvar; + } + } + // Reset the value of smoothed RTT to the first real RTT estimate extracted + // from an ACK after initialization (at the beginning of transmission). + // In case of resumed connection over the same network, the very first RTT + // value sent within an ACK will be taken from cache and equal to previous + // connection's final smoothed RTT value. The reception of such a value + // will also trigger the smoothed RTT reset at the sender side. + else if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + { + m_iRTT = rtt; + m_iRTTVar = rttvar; + m_bIsFirstRTTReceived = true; + } + +#if SRT_DEBUG_RTT + s_rtt_trace.trace(currtime, "ACK", rtt, rttvar, m_bIsFirstRTTReceived, + m_stats.recvTotal, m_iRTT, m_iRTTVar); +#endif /* Version-dependent fields: * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL): @@ -8062,7 +8194,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival { int32_t ack = 0; - // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair. const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, tsArrival); if (rtt == -1) @@ -8091,12 +8223,32 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival return; } - // If increasing delay is detected + // If increasing delay is detected. // sendCtrl(UMSG_CGWARNING); - // Calculate RTT (EWMA) on the receiver side - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + // Update the values of smoothed RTT and the variation in RTT samples + // on subsequent RTT samples (during transmission). + if (m_bIsFirstRTTReceived) + { + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + } + // Reset the value of smoothed RTT on the first RTT sample after initialization + // (at the beginning of transmission). + // In case of resumed connection over the same network, the initial RTT + // value will be taken from cache and equal to previous connection's + // final smoothed RTT value. + else + { + m_iRTT = rtt; + m_iRTTVar = rtt / 2; + m_bIsFirstRTTReceived = true; + } + +#if SRT_DEBUG_RTT + s_rtt_trace.trace(tsArrival, "ACKACK", rtt, -1, m_bIsFirstRTTReceived, + -1, m_iRTT, m_iRTTVar); +#endif updateCC(TEV_ACKACK, EventVariant(ack)); diff --git a/srtcore/core.h b/srtcore/core.h index b3498c814..a89c23333 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -267,13 +267,15 @@ class CUDT // // NOTE: Use notation with X*1000*1000*... instead of // million zeros in a row. - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; - static const size_t ACK_WND_SIZE = 1024; + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; + static const int INITIAL_RTT = 10 * COMM_SYN_INTERVAL_US; + static const int INITIAL_RTTVAR = INITIAL_RTT / 2; int handshakeVersion() { @@ -731,8 +733,14 @@ class CUDT int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iRTT; // RTT, in microseconds - int m_iRTTVar; // RTT variance + int m_iRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + // of an endpoint's RTT samples), in microseconds + int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair + // at the receiver side or received by the sender from an ACK packet. + // It's used to reset the initial value of smoothed RTT (m_iRTT) + // at the beginning of transmission (including the one taken from + // cache). False by default. int m_iDeliveryRate; // Packet arrival rate at the receiver side int m_iByteDeliveryRate; // Byte arrival rate at the receiver side From 782a27f2328c8e9fbd4481dff56b11ecbea74e9f Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Thu, 29 Apr 2021 10:00:54 +0200 Subject: [PATCH 156/790] [core] Renamed m_iRTT variable to m_iSRTT (#1971) --- docs/API/statistics.md | 19 +++---------- srtcore/cache.cpp | 28 +++++++++--------- srtcore/cache.h | 18 ++++++------ srtcore/common.h | 2 +- srtcore/congctl.cpp | 18 ++++++------ srtcore/congctl.h | 2 +- srtcore/core.cpp | 64 +++++++++++++++++++++--------------------- srtcore/core.h | 6 ++-- srtcore/crypto.cpp | 2 +- srtcore/group.cpp | 4 +-- 10 files changed, 76 insertions(+), 87 deletions(-) diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 156224fd0..334437bc7 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -539,21 +539,10 @@ at that moment. #### msRTT -Calculated Round trip time (RTT), in milliseconds. Sender and Receiver. \ -The value is calculated by the receiver based on the incoming ACKACK control packets -(used by sender to acknowledge ACKs from receiver). - -The RTT (Round-Trip time) is the sum of two STT (Single-Trip time) -values, one from agent to peer, and one from peer to agent. Note that **the -measurement method is different than in TCP**. SRT measures only the "reverse -RTT", that is, the time measured at the receiver between sending a `UMSG_ACK` -message until receiving the sender's `UMSG_ACKACK` response message (with the -same journal). This happens to be a little different from the "forward RTT" -measured in TCP, which is the time between sending a data packet of a particular -sequence number and receiving `UMSG_ACK` with a sequence number that is later -by 1. Forward RTT isn't being measured or reported in SRT, although some -research works have shown that these values, even though they should be the same, -happen to differ; "reverse RTT" seems to be more optimistic. +Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds. +Available both for sender and receiver. + +See [Section 4.10. Round-Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and [[RFC6298] Paxson, V.,Ā Allman, M.,Ā Chu, J., andĀ M. Sargent,Ā "Computing TCP's Retransmission Timer"](https://datatracker.ietf.org/doc/html/rfc6298) for more details. #### mbpsBandwidth diff --git a/srtcore/cache.cpp b/srtcore/cache.cpp index bc9640655..6dfcbba1f 100644 --- a/srtcore/cache.cpp +++ b/srtcore/cache.cpp @@ -50,14 +50,14 @@ using namespace std; CInfoBlock& CInfoBlock::copyFrom(const CInfoBlock& obj) { std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP); - m_iIPversion = obj.m_iIPversion; - m_ullTimeStamp = obj.m_ullTimeStamp; - m_iRTT = obj.m_iRTT; - m_iBandwidth = obj.m_iBandwidth; - m_iLossRate = obj.m_iLossRate; + m_iIPversion = obj.m_iIPversion; + m_ullTimeStamp = obj.m_ullTimeStamp; + m_iSRTT = obj.m_iSRTT; + m_iBandwidth = obj.m_iBandwidth; + m_iLossRate = obj.m_iLossRate; m_iReorderDistance = obj.m_iReorderDistance; - m_dInterval = obj.m_dInterval; - m_dCWnd = obj.m_dCWnd; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; return *this; } @@ -84,14 +84,14 @@ CInfoBlock* CInfoBlock::clone() CInfoBlock* obj = new CInfoBlock; std::copy(m_piIP, m_piIP + 4, obj->m_piIP); - obj->m_iIPversion = m_iIPversion; - obj->m_ullTimeStamp = m_ullTimeStamp; - obj->m_iRTT = m_iRTT; - obj->m_iBandwidth = m_iBandwidth; - obj->m_iLossRate = m_iLossRate; + obj->m_iIPversion = m_iIPversion; + obj->m_ullTimeStamp = m_ullTimeStamp; + obj->m_iSRTT = m_iSRTT; + obj->m_iBandwidth = m_iBandwidth; + obj->m_iLossRate = m_iLossRate; obj->m_iReorderDistance = m_iReorderDistance; - obj->m_dInterval = m_dInterval; - obj->m_dCWnd = m_dCWnd; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; return obj; } diff --git a/srtcore/cache.h b/srtcore/cache.h index 71ec4435a..a39313f61 100644 --- a/srtcore/cache.h +++ b/srtcore/cache.h @@ -235,15 +235,15 @@ template class CCache class CInfoBlock { public: - uint32_t m_piIP[4]; // IP address, machine read only, not human readable format - int m_iIPversion; // Address family: AF_INET or AF_INET6 - uint64_t m_ullTimeStamp; // last update time - int m_iRTT; // RTT - int m_iBandwidth; // estimated bandwidth - int m_iLossRate; // average loss rate - int m_iReorderDistance; // packet reordering distance - double m_dInterval; // inter-packet time, congestion control - double m_dCWnd; // congestion window size, congestion control + uint32_t m_piIP[4]; // IP address, machine read only, not human readable format. + int m_iIPversion; // Address family: AF_INET or AF_INET6. + uint64_t m_ullTimeStamp; // Last update time. + int m_iSRTT; // Smoothed RTT. + int m_iBandwidth; // Estimated link bandwidth. + int m_iLossRate; // Average loss rate. + int m_iReorderDistance; // Packet reordering distance. + double m_dInterval; // Inter-packet time (Congestion Control). + double m_dCWnd; // Congestion window size (Congestion Control). public: CInfoBlock() {} // NOTE: leaves uninitialized diff --git a/srtcore/common.h b/srtcore/common.h index 6c9948351..8a47a0c6c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -284,7 +284,7 @@ enum ETransmissionEvent { TEV_INIT, // --> After creation, and after any parameters were updated. TEV_ACK, // --> When handling UMSG_ACK - older CCC:onAck() - TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::RTT(). + TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::SRTT(). TEV_LOSSREPORT, // --> When handling UMSG_LOSSREPORT - older CCC::onLoss() TEV_CHECKTIMER, // --> See TEV_CHT_REXMIT TEV_SEND, // --> When the packet is scheduled for sending - older CCC::onPktSent diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 721542c92..0b00929b7 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -358,11 +358,11 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize=" << m_dCWndSize << "/" << m_dMaxCWndSize << " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT=" - << m_parent->RTT() << " RCIV=" << m_iRCInterval); + << m_parent->SRTT() << " RCIV=" << m_iRCInterval); } } else @@ -374,9 +374,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16; + m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->SRTT() + m_iRCInterval) + 16; HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize=" - << m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT() + << m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->SRTT() << " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = " << m_parent->deliveryRate() << " pkts/s)"); } @@ -481,9 +481,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT=" - << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")"); + << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } @@ -491,7 +491,7 @@ class FileCC : public SrtCongestionControlBase m_bLoss = true; // TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo()); - const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod; + const int pktsInFlight = m_parent->SRTT() / m_dPktSndPeriod; const int numPktsLost = m_parent->sndLossLength(); const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0; @@ -581,9 +581,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")"); + << setprecision(6) << m_dCWndSize << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 4f887f8be..6419605c0 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -146,7 +146,7 @@ class SrtCongestionControlBase //int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead. //int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo(). //int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead. - //int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead. + //int m_RTT; // NOT REQUIRED. Use m_parent->SRTT() instead. //char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead. // Constructor in protected section so that this class is semi-abstract. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7f23e2653..c4ff3545c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -932,7 +932,7 @@ void CUDT::open() m_pRNode->m_bOnList = false; // Set initial values of smoothed RTT and RTT variance. - m_iRTT = INITIAL_RTT; + m_iSRTT = INITIAL_RTT; m_iRTTVar = INITIAL_RTTVAR; m_bIsFirstRTTReceived = false; @@ -3707,7 +3707,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) log << "startConnect: END. Parameters:" " mss=" << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() - << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); + << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); } // Asynchronous connection @@ -4553,14 +4553,14 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE CInfoBlock::convert(m_PeerAddr, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { - m_iRTT = ib.m_iRTT; - m_iRTTVar = ib.m_iRTT / 2; + m_iSRTT = ib.m_iSRTT; + m_iRTTVar = ib.m_iSRTT / 2; m_iBandwidth = ib.m_iBandwidth; } #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Connect", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); + m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar); #endif SRT_REJECT_REASON rr = setupCC(); @@ -5457,14 +5457,14 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, CInfoBlock::convert(peer, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { - m_iRTT = ib.m_iRTT; - m_iRTTVar = ib.m_iRTT / 2; + m_iSRTT = ib.m_iSRTT; + m_iRTTVar = ib.m_iSRTT / 2; m_iBandwidth = ib.m_iBandwidth; } #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Accept", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); + m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar); #endif m_PeerAddr = peer; @@ -5683,7 +5683,7 @@ SRT_REJECT_REASON CUDT::setupCC() HLOGC(rslog.Debug, log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" - << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); + << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET))) { @@ -5749,7 +5749,7 @@ void CUDT::checkSndTimers(Whether2RegenKm regen) { HLOGC(cnlog.Debug, log << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase"); // Legacy method for HSREQ, only if initiator. - considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iRTT * 3 / 2)); + considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iSRTT * 3 / 2)); } else { @@ -5926,13 +5926,13 @@ bool CUDT::closeInternal() CInfoBlock ib; ib.m_iIPversion = m_PeerAddr.family(); CInfoBlock::convert(m_PeerAddr, ib.m_piIP); - ib.m_iRTT = m_iRTT; + ib.m_iSRTT = m_iSRTT; ib.m_iBandwidth = m_iBandwidth; m_pCache->update(&ib); #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Cache", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, -1); + m_bIsFirstRTTReceived, -1, m_iSRTT, -1); #endif m_bConnected = false; @@ -7115,7 +7115,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); - perf->msRTT = (double)m_iRTT / 1000.0; + perf->msRTT = (double)m_iSRTT / 1000.0; perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; perf->byteMSS = m_config.iMSS; @@ -7529,7 +7529,7 @@ void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } // update next NAK time, which should wait enough time for the retansmission, but not too long - m_tdNAKInterval = microseconds_from(m_iRTT + 4 * m_iRTTVar); + m_tdNAKInterval = microseconds_from(m_iSRTT + 4 * m_iRTTVar); // Fix the NAKreport period according to the congctl m_tdNAKInterval = @@ -7761,7 +7761,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { // If the ACK was just sent already AND elapsed time did not exceed RTT, if ((steady_clock::now() - m_tsLastAckTime) < - (microseconds_from(m_iRTT + 4 * m_iRTTVar))) + (microseconds_from(m_iSRTT + 4 * m_iRTTVar))) { HLOGC(xtlog.Debug, log << "sendCtrl(UMSG_ACK): ACK %" << ack << " just sent - too early to repeat"); return nbsent; @@ -7792,7 +7792,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // number), but still the numbers are from exactly the same domain. m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); data[ACKD_RCVLASTACK] = m_iRcvLastAck; - data[ACKD_RTT] = m_iRTT; + data[ACKD_RTT] = m_iSRTT; data[ACKD_RTTVAR] = m_iRTTVar; data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock @@ -8110,15 +8110,15 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); + m_iSRTT = avg_iir<8>(m_iSRTT, rtt); } } else // Transmission is unidirectional. { // Simply take the values of smoothed RTT and RTT variance from // the ACK packet. - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rttvar; } } @@ -8130,14 +8130,14 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point // will also trigger the smoothed RTT reset at the sender side. else if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rttvar; m_bIsFirstRTTReceived = true; } #if SRT_DEBUG_RTT s_rtt_trace.trace(currtime, "ACK", rtt, rttvar, m_bIsFirstRTTReceived, - m_stats.recvTotal, m_iRTT, m_iRTTVar); + m_stats.recvTotal, m_iSRTT, m_iRTTVar); #endif /* Version-dependent fields: @@ -8204,14 +8204,14 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival LOGC(inlog.Warn, log << CONID() << "ACKACK out of order, skipping RTT calculation " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } LOGC(inlog.Error, log << CONID() << "IPE: ACK record not found, can't estimate RTT " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } @@ -8230,8 +8230,8 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival // on subsequent RTT samples (during transmission). if (m_bIsFirstRTTReceived) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); + m_iSRTT = avg_iir<8>(m_iSRTT, rtt); } // Reset the value of smoothed RTT on the first RTT sample after initialization // (at the beginning of transmission). @@ -8240,14 +8240,14 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival // final smoothed RTT value. else { - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rtt / 2; m_bIsFirstRTTReceived = true; } #if SRT_DEBUG_RTT s_rtt_trace.trace(tsArrival, "ACKACK", rtt, -1, m_bIsFirstRTTReceived, - -1, m_iRTT, m_iRTTVar); + -1, m_iSRTT, m_iRTTVar); #endif updateCC(TEV_ACKACK, EventVariant(ack)); @@ -8725,7 +8725,7 @@ void CUDT::updateSrtSndSettings() { /* We are TsbPd sender */ // XXX Check what happened here. - // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iRTT + (4 * m_iRTTVar)) / 1000); + // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iSRTT + (4 * m_iRTTVar)) / 1000); /* * For sender to apply Too-Late Packet Drop * option (m_bTLPktDrop) must be enabled and receiving peer shall support it @@ -8802,7 +8802,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime // protect m_iSndLastDataAck from updating by ACK processing UniqueLock ackguard(m_RecvAckLock); const steady_clock::time_point time_now = steady_clock::now(); - const steady_clock::time_point time_nak = time_now - microseconds_from(m_iRTT - 4 * m_iRTTVar); + 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) { @@ -8841,7 +8841,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) - << " RTT=" << m_iRTT << " RTTVar=" << m_iRTTVar + << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; } @@ -10758,7 +10758,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea else { steady_clock::duration exp_timeout = - microseconds_from(m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); + microseconds_from(m_iEXPCount * (m_iSRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); if (exp_timeout < (m_iEXPCount * m_tdMinExpInterval)) exp_timeout = m_iEXPCount * m_tdMinExpInterval; next_exp_time = m_tsLastRspTime + exp_timeout; @@ -10834,7 +10834,7 @@ void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) * in the sender's buffer will be added to loss list and retransmitted. */ - const uint64_t rtt_syn = (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); + const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); if (currtime <= (m_tsLastRspAckTime + microseconds_from(exp_int_us))) diff --git a/srtcore/core.h b/srtcore/core.h index a89c23333..dbb1ab5f9 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -302,7 +302,7 @@ class CUDT void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); bool isOPT_TsbPd() const { return m_config.bTSBPD; } - int RTT() const { return m_iRTT; } + int SRTT() const { return m_iSRTT; } int RTTVar() const { return m_iRTTVar; } int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } int32_t schedSeqNo() const { return m_iSndNextSeqNo; } @@ -733,12 +733,12 @@ class CUDT int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + int m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. - // It's used to reset the initial value of smoothed RTT (m_iRTT) + // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. int m_iDeliveryRate; // Packet arrival rate at the receiver side diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 4753e7338..5351e1afe 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -439,7 +439,7 @@ void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED) * then (re-)send handshake request. */ if (((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0)) - && ((m_SndKmLastTime + srt::sync::microseconds_from((m_parent->RTT() * 3)/2)) <= now)) + && ((m_SndKmLastTime + srt::sync::microseconds_from((m_parent->SRTT() * 3)/2)) <= now)) { for (int ki = 0; ki < 2; ki++) { diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 032ac1580..fbd9664b2 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2978,7 +2978,7 @@ class StabilityTracer m_fout << u.id() << ","; m_fout << weight << ","; m_fout << u.peerLatency_us() << ","; - m_fout << u.RTT() << ","; + m_fout << u.SRTT() << ","; m_fout << u.RTTVar() << ","; m_fout << stability_tmo_us << ","; m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; @@ -3039,7 +3039,7 @@ static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::ste const int64_t stability_tout_us = is_activation_phase ? initial_stabtout_us // activation phase - : min(max(min_stability_us, 2 * u.RTT() + 4 * u.RTTVar()), latency_us); + : min(max(min_stability_us, 2 * u.SRTT() + 4 * u.RTTVar()), latency_us); const steady_clock::time_point last_rsp = max(u.freshActivationStart(), u.lastRspTime()); const steady_clock::duration td_response = currtime - last_rsp; From 61dda69be32408192bf6c628a8f3ba5839802aa7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 29 Apr 2021 11:53:32 +0200 Subject: [PATCH 157/790] [apps] Fixed nonblocking client example (#1973) - Fixed setting SRTO_RCVSYN and SRTO_SNDSYN to 0. - Print reject reason. --- examples/example-client-nonblock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/example-client-nonblock.c b/examples/example-client-nonblock.c index b711f5a38..bbaeb0c44 100644 --- a/examples/example-client-nonblock.c +++ b/examples/example-client-nonblock.c @@ -31,7 +31,7 @@ int main(int argc, char** argv) { int ss, st; struct sockaddr_in sa; - int yes = 1; + const int no = 0; const char message [] = "This message should be sent to the other side"; if (argc != 3) { @@ -66,8 +66,8 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) - || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &yes, sizeof yes)) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &no, sizeof no) + || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &no, sizeof no)) { fprintf(stderr, "SRTO_SNDSYN or SRTO_RCVSYN: %s\n", srt_getlasterror_str()); return 1; @@ -101,7 +101,7 @@ int main(int argc, char** argv) SRT_SOCKSTATUS state = srt_getsockstate(ss); if (state != SRTS_CONNECTED || rlen > 0) // rlen > 0 - an error notification { - fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + fprintf(stderr, "srt_epoll_wait: reject reason %s\n", srt_rejectreason_str(srt_getrejectreason(rready))); return 1; } From 9848d68371e22b29f9e32c52ad6cd0e1bb81cc88 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 29 Apr 2021 11:54:12 +0200 Subject: [PATCH 158/790] [core] TSBPD logic extracted from CRcvBuffer. (#1968) Refactoring, no functional changes are expected. New CTsbpdTime class to operate with TSBPD timing. --- srtcore/buffer.cpp | 301 +++-------------------------------------- srtcore/buffer.h | 56 ++------ srtcore/core.cpp | 2 +- srtcore/filelist.maf | 6 +- srtcore/group.cpp | 5 +- srtcore/tsbpd_time.cpp | 164 ++++++++++++++++++++++ srtcore/tsbpd_time.h | 161 ++++++++++++++++++++++ 7 files changed, 360 insertions(+), 335 deletions(-) create mode 100644 srtcore/tsbpd_time.cpp create mode 100644 srtcore/tsbpd_time.h diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 20f5da3d8..660f2b335 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -817,9 +817,6 @@ CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) , m_iAckedPktsCount(0) , m_iAckedBytesCount(0) , m_uAvgPayloadSz(7 * 188) - , m_bTsbPdMode(false) - , m_tdTsbPdDelay(0) - , m_bTsbPdWrapCheck(false) { m_pUnit = new CUnit*[m_iSize]; for (int i = 0; i < m_iSize; ++i) @@ -909,7 +906,8 @@ int CRcvBuffer::readBuffer(char* data, int len) int rs = len; IF_HEAVY_LOGGING(char* begin = data); - const steady_clock::time_point now = (m_bTsbPdMode ? steady_clock::now() : steady_clock::time_point()); + const bool bTsbPdEnabled = m_tsbpd.isEnabled(); + const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); HLOGC(brlog.Debug, log << CONID() << "readBuffer: start=" << p << " lastack=" << lastack); while ((p != lastack) && (rs > 0)) @@ -922,7 +920,7 @@ int CRcvBuffer::readBuffer(char* data, int len) const CPacket& pkt = m_pUnit[p]->m_Packet; - if (m_bTsbPdMode) + if (bTsbPdEnabled) { HLOGC(brlog.Debug, log << CONID() << "readBuffer: chk if time2play:" @@ -1427,7 +1425,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& { w_tsbpdtime = steady_clock::time_point(); - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { const CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) @@ -1649,7 +1647,7 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) { timespan = 0; - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { // Get a valid startpos. // Skip invalid entries in the beginning, if any. @@ -1731,306 +1729,45 @@ void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) m_pUnit[i]->m_iFlag = CUnit::DROPPED; } -steady_clock::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t timestamp_us) -{ - /* - * Packet timestamps wrap around every 01h11m35s (32-bit in usec) - * When added to the peer start time (base time), - * wrapped around timestamps don't provide a valid local packet delevery time. - * - * A wrap check period starts 30 seconds before the wrap point. - * In this period, timestamps smaller than 30 seconds are considered to have wrapped around (then adjusted). - * The wrap check period ends 30 seconds after the wrap point, afterwhich time base has been adjusted. - */ - int64_t carryover = 0; - - // This function should generally return the timebase for the given timestamp_us. - // It's assumed that the timestamp_us, for which this function is being called, - // is received as monotonic clock. This function then traces the changes in the - // timestamps passed as argument and catches the moment when the 64-bit timebase - // should be increased by a "segment length" (MAX_TIMESTAMP+1). - - // The checks will be provided for the following split: - // [INITIAL30][FOLLOWING30]....[LAST30] <-- == CPacket::MAX_TIMESTAMP - // - // The following actions should be taken: - // 1. Check if this is [LAST30]. If so, ENTER TSBPD-wrap-check state - // 2. Then, it should turn into [INITIAL30] at some point. If so, use carryover MAX+1. - // 3. Then it should switch to [FOLLOWING30]. If this is detected, - // - EXIT TSBPD-wrap-check state - // - save the carryover as the current time base. - - if (m_bTsbPdWrapCheck) - { - // Wrap check period. - - if (timestamp_us < TSBPD_WRAP_PERIOD) - { - carryover = int64_t(CPacket::MAX_TIMESTAMP) + 1; - } - // timestamp_us >= TSBPD_WRAP_PERIOD - else if (timestamp_us <= (TSBPD_WRAP_PERIOD * 2)) - { - /* Exiting wrap check period (if for packet delivery head) */ - m_bTsbPdWrapCheck = false; - m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1); - LOGC(tslog.Debug, - log << "tsbpd wrap period ends with ts=" << timestamp_us << " - NEW TIME BASE: " - << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us"); - } - } - // Check if timestamp_us is in the last 30 seconds before reaching the MAX_TIMESTAMP. - else if (timestamp_us > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) - { - /* Approching wrap around point, start wrap check period (if for packet delivery head) */ - m_bTsbPdWrapCheck = true; - LOGC(tslog.Debug, - log << "tsbpd wrap period begins with ts=" << timestamp_us << " drift: " << m_DriftTracer.drift() - << "us."); - } - - return (m_tsTsbPdTimeBase + microseconds_from(carryover)); -} - void CRcvBuffer::applyGroupTime(const steady_clock::time_point& timebase, bool wrp, uint32_t delay, const steady_clock::duration& udrift) { - // Same as setRcvTsbPdMode, but predicted to be used for group members. - // This synchronizes the time from the INTERNAL TIMEBASE of an existing - // socket's internal timebase. This is required because the initial time - // base stays always the same, whereas the internal timebase undergoes - // adjustment as the 32-bit timestamps in the sockets wrap. The socket - // newly added to the group must get EXACTLY the same internal timebase - // or otherwise the TsbPd time calculation will ship different results - // on different sockets. - - m_bTsbPdMode = true; - - m_tsTsbPdTimeBase = timebase; - m_bTsbPdWrapCheck = wrp; - m_tdTsbPdDelay = microseconds_from(delay); - m_DriftTracer.forceDrift(count_microseconds(udrift)); + m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); } void CRcvBuffer::applyGroupDrift(const steady_clock::time_point& timebase, bool wrp, const steady_clock::duration& udrift) { - // This is only when a drift was updated on one of the group members. - HLOGC(brlog.Debug, - log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift) - << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase)); - - m_tsTsbPdTimeBase = timebase; - m_bTsbPdWrapCheck = wrp; - - m_DriftTracer.forceDrift(count_microseconds(udrift)); + m_tsbpd.applyGroupDrift(timebase, wrp, udrift); } -bool CRcvBuffer::getInternalTimeBase(steady_clock::time_point& w_timebase, steady_clock::duration& w_udrift) +void CRcvBuffer::getInternalTimeBase(steady_clock::time_point& w_timebase, bool& w_wrp, steady_clock::duration& w_udrift) { - w_timebase = m_tsTsbPdTimeBase; - w_udrift = microseconds_from(m_DriftTracer.drift()); - return m_bTsbPdWrapCheck; -} - -steady_clock::time_point CRcvBuffer::getPktTsbPdTime(uint32_t timestamp) -{ - const steady_clock::time_point time_base = getTsbPdTimeBase(timestamp); - - // Display only ingredients, not the result, as the result will - // be displayed anyway in the next logs. - HLOGC(brlog.Debug, - log << "getPktTsbPdTime: TIMEBASE=" << FormatTime(time_base) << " + dTS=" << timestamp - << "us + LATENCY=" << FormatDuration(m_tdTsbPdDelay) << " + uDRIFT=" << m_DriftTracer.drift()); - return (time_base + m_tdTsbPdDelay + microseconds_from(timestamp + m_DriftTracer.drift())); + return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift); } -int CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const steady_clock::duration& delay) +steady_clock::time_point CRcvBuffer::getPktTsbPdTime(uint32_t usPktTimestamp) { - m_bTsbPdMode = true; - m_bTsbPdWrapCheck = false; - - // Timebase passed here comes is calculated as: - // >>> CTimer::getTime() - ctrlpkt->m_iTimeStamp - // where ctrlpkt is the packet with SRT_CMD_HSREQ message. - // - // This function is called in the HSREQ reception handler only. - m_tsTsbPdTimeBase = timebase; - // XXX Seems like this may not work correctly. - // At least this solution this way won't work with application-supplied - // timestamps. For that case the timestamps should be taken exclusively - // from the data packets because in case of application-supplied timestamps - // they come from completely different server and undergo different rules - // of network latency and drift. - m_tdTsbPdDelay = delay; - return 0; -} - -#ifdef SRT_DEBUG_TSBPD_DRIFT -void CRcvBuffer::printDriftHistogram(int64_t iDrift) -{ - /* - * Build histogram of drift values - * First line (ms): <=-10.0 -9.0 ... -1.0 - 0.0 + 1.0 ... 9.0 >=10.0 - * Second line (ms): -0.9 ... -0.1 - 0.0 + 0.1 ... 0.9 - * 0 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 1 0 0 0 0 0 0 - * 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 0 0 0 0 0 0 - */ - iDrift /= 100; // uSec to 100 uSec (0.1ms) - if (-10 < iDrift && iDrift < 10) - { - /* Fill 100us histogram -900 .. 900 us 100 us increments */ - m_TsbPdDriftHisto100us[10 + iDrift]++; - } - else - { - /* Fill 1ms histogram <=-10.0, -9.0 .. 9.0, >=10.0 ms in 1 ms increments */ - iDrift /= 10; // 100uSec to 1ms - if (-10 < iDrift && iDrift < 10) - m_TsbPdDriftHisto1ms[10 + iDrift]++; - else if (iDrift <= -10) - m_TsbPdDriftHisto1ms[0]++; - else - m_TsbPdDriftHisto1ms[20]++; - } - ++m_iTsbPdDriftNbSamples; - if ((m_iTsbPdDriftNbSamples % TSBPD_DRIFT_PRT_SAMPLES) == 0) - { - int* histo = m_TsbPdDriftHisto1ms; - - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", - histo[0], - histo[1], - histo[2], - histo[3], - histo[4], - histo[5], - histo[6], - histo[7], - histo[8], - histo[9], - histo[10]); - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n", - histo[11], - histo[12], - histo[13], - histo[14], - histo[15], - histo[16], - histo[17], - histo[18], - histo[19], - histo[20]); - - histo = m_TsbPdDriftHisto100us; - fprintf(stderr, - " %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", - histo[1], - histo[2], - histo[3], - histo[4], - histo[5], - histo[6], - histo[7], - histo[8], - histo[9], - histo[10]); - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d\n", - histo[11], - histo[12], - histo[13], - histo[14], - histo[15], - histo[16], - histo[17], - histo[18], - histo[19]); - - m_iTsbPdDriftNbSamples = 0; - } + // Updating TSBPD time here is not very accurate and prevents from making the function constant. + // For now preserving the existing behavior. + m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); + return m_tsbpd.getPktTsbPdTime(usPktTimestamp); } -void CRcvBuffer::printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg) +void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const steady_clock::duration& delay) { - fprintf(stderr, - "%s: tsbpd offset=%d drift=%d usec\n", - FormatTime(steady_clock::now()).c_str(), - tsbPdOffset, - tsbPdDriftAvg); - memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); - memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); + const bool no_wrap_check = false; + m_tsbpd.setTsbPdMode(timebase, no_wrap_check, delay); } -#endif /* SRT_DEBUG_TSBPD_DRIFT */ bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, - Mutex& mutex_to_lock, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { - if (!m_bTsbPdMode) // Not checked unless in TSBPD mode - return false; - /* - * TsbPD time drift correction - * TsbPD time slowly drift over long period depleting decoder buffer or raising latency - * Re-evaluate the time adjustment value using a receiver control packet (ACK-ACK). - * ACK-ACK timestamp is RTT/2 ago (in sender's time base) - * Data packet have origin time stamp which is older when retransmitted so not suitable for this. - * - * Every TSBPD_DRIFT_MAX_SAMPLES packets, the average drift is calculated - * if -TSBPD_DRIFT_MAX_VALUE < avgTsbPdDrift < TSBPD_DRIFT_MAX_VALUE uSec, pass drift value to RcvBuffer to adjust - * delevery time. if outside this range, adjust this->TsbPdTimeOffset and RcvBuffer->TsbPdTimeBase by - * +-TSBPD_DRIFT_MAX_VALUE uSec to maintain TsbPdDrift values in reasonable range (-5ms .. +5ms). - */ - - // Note important thing: this function is being called _EXCLUSIVELY_ in the handler - // of UMSG_ACKACK command reception. This means that the timestamp used here comes - // from the CONTROL domain, not DATA domain (timestamps from DATA domain may be - // either schedule time or a time supplied by the application). - - const steady_clock::duration iDrift = - steady_clock::now() - (getTsbPdTimeBase(timestamp_us) + microseconds_from(timestamp_us)); - - enterCS(mutex_to_lock); - - bool updated = m_DriftTracer.update(count_microseconds(iDrift)); - -#ifdef SRT_DEBUG_TSBPD_DRIFT - printDriftHistogram(count_microseconds(iDrift)); -#endif /* SRT_DEBUG_TSBPD_DRIFT */ - - if (updated) - { -#ifdef SRT_DEBUG_TSBPD_DRIFT - printDriftOffset(m_DriftTracer.overdrift(), m_DriftTracer.drift()); -#endif /* SRT_DEBUG_TSBPD_DRIFT */ - -#if ENABLE_HEAVY_LOGGING - const steady_clock::time_point oldbase = m_tsTsbPdTimeBase; -#endif - steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift()); - m_tsTsbPdTimeBase += overdrift; - - HLOGC(brlog.Debug, - log << "DRIFT=" << FormatDuration(iDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0) - << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift) - << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase)); - } - else - { - HLOGC(brlog.Debug, - log << "DRIFT=" << FormatDuration(iDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); - } - - leaveCS(mutex_to_lock); - w_udrift = iDrift; - w_newtimebase = m_tsTsbPdTimeBase; - return updated; + return m_tsbpd.addDriftSample(timestamp_us, w_udrift, w_newtimebase); } int CRcvBuffer::readMsg(char* data, int len) @@ -2088,7 +1825,7 @@ bool CRcvBuffer::accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playt bool empty = true; - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { w_passack = false; int seq = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index d998fd1b9..6a21df1ce 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -56,8 +56,8 @@ modified by #include "udt.h" #include "list.h" #include "queue.h" +#include "tsbpd_time.h" #include "utilities.h" -#include // The notation used for "circular numbers" in comments: // The "cicrular numbers" are numbers that when increased up to the @@ -392,14 +392,13 @@ class CRcvBuffer /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay /// @param [in] delay aggreed TsbPD delay - /// @return 0 - int setRcvTsbPdMode(const time_point& timebase, const duration& delay); + void setRcvTsbPdMode(const time_point& timebase, const duration& delay); /// Add packet timestamp for drift caclculation and compensation /// @param [in] timestamp packet time stamp - /// @param [ref] lock Mutex that should be locked for the operation + /// @param [out] w_udrift current drift value + /// @param [out] w_newtimebase current TSBPD base time bool addRcvTsbPdDriftSample(uint32_t timestamp, - srt::sync::Mutex& mutex_to_lock, duration& w_udrift, time_point& w_newtimebase); @@ -463,19 +462,12 @@ class CRcvBuffer bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: - /// Get packet delivery local time base (adjusted for wrap around) - /// (Exposed as used publicly in logs) - /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min - /// @return local delivery time (usec) - time_point getTsbPdTimeBase(uint32_t timestamp_us); - - int64_t getDrift() const { return m_DriftTracer.drift(); } + int64_t getDrift() const { return m_tsbpd.drift(); } public: int32_t getTopMsgno() const; - // @return Wrap check value - bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); + void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift); void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); @@ -540,41 +532,9 @@ class CRcvBuffer int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode - duration m_tdTsbPdDelay; // aggreed delay - time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode - // Note: m_tsTsbPdTimeBase cumulates values from: - // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: - // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception - // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected - // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). - // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively - // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE - // once the value of average drift exceeds this value in whatever direction. - // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE - // - // XXX Application-supplied timestamps won't work therefore. This requires separate - // calculation of all these things above. - - bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around - static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) - - /// Max drift (usec) above which TsbPD Time Offset is adjusted - static const int TSBPD_DRIFT_MAX_VALUE = 5000; - /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation - static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; - DriftTracer m_DriftTracer; - AvgBufSize m_mavg; -#ifdef SRT_DEBUG_TSBPD_DRIFT - int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) - int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) - int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram - static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram -#endif /* SRT_DEBUG_TSBPD_DRIFT */ + srt::CTsbpdTime m_tsbpd; -#ifdef SRT_DEBUG_TSBPD_OUTJITTER - unsigned long m_ulPdHisto[4][10]; -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + AvgBufSize m_mavg; private: CRcvBuffer(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c4ff3545c..c87efeb42 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8262,7 +8262,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, + const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 4064c265f..400541499 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -19,12 +19,13 @@ packet.cpp packetfilter.cpp queue.cpp congctl.cpp -srt_c_api.cpp -window.cpp socketconfig.cpp +srt_c_api.cpp srt_compat.c strerror_defs.cpp sync.cpp +tsbpd_time.cpp +window.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp @@ -69,6 +70,7 @@ congctl.h socketconfig.h srt_compat.h threadname.h +tsbpd_time.h utilities.h window.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index fbd9664b2..d6dee49f9 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -48,7 +48,7 @@ bool CUDTGroup::getBufferTimeBase(CUDT* forthesakeof, if (!master) return false; - w_wp = master->m_pRcvBuffer->getInternalTimeBase((w_tb), (w_dr)); + master->m_pRcvBuffer->getInternalTimeBase((w_tb), (w_wp), (w_dr)); // Sanity check if (is_zero(w_tb)) @@ -2734,7 +2734,8 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady steady_clock::time_point this_timebase; steady_clock::duration this_udrift(0); - bool wrp = gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (this_udrift)); + bool wrp = false; + gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); udrift = std::min(udrift, this_udrift); steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp new file mode 100644 index 000000000..0fe07fb7c --- /dev/null +++ b/srtcore/tsbpd_time.cpp @@ -0,0 +1,164 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +#include "tsbpd_time.h" + +#include "logging.h" +#include "logger_defs.h" +#include "packet.h" + +using namespace srt_logging; +using namespace srt::sync; + +namespace srt +{ + +bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, + steady_clock::duration& w_udrift, + steady_clock::time_point& w_newtimebase) +{ + if (!m_bTsbPdMode) + return false; + + const time_point tsNow = steady_clock::now(); + + ScopedLock lck(m_mtxRW); + const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp); + + const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); + + if (updated) + { + IF_HEAVY_LOGGING(const steady_clock::time_point oldbase = m_tsTsbPdTimeBase); + steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift()); + m_tsTsbPdTimeBase += overdrift; + + HLOGC(brlog.Debug, + log << "DRIFT=" << FormatDuration(tdDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0) + << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift) + << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase)); + } + else + { + HLOGC(brlog.Debug, + log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); + } + + w_udrift = tdDrift; + w_newtimebase = m_tsTsbPdTimeBase; + + return updated; +} + +void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) +{ + m_bTsbPdMode = true; + m_bTsbPdWrapCheck = wrap; + + // Timebase passed here comes is calculated as: + // Tnow - hspkt.m_iTimeStamp + // where hspkt is the packet with SRT_CMD_HSREQ message. + // + // This function is called in the HSREQ reception handler only. + m_tsTsbPdTimeBase = timebase; + m_tdTsbPdDelay = delay; +} + +void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, + bool wrp, + uint32_t delay, + const steady_clock::duration& udrift) +{ + // Same as setTsbPdMode, but predicted to be used for group members. + // This synchronizes the time from the INTERNAL TIMEBASE of an existing + // socket's internal timebase. This is required because the initial time + // base stays always the same, whereas the internal timebase undergoes + // adjustment as the 32-bit timestamps in the sockets wrap. The socket + // newly added to the group must get EXACTLY the same internal timebase + // or otherwise the TsbPd time calculation will ship different results + // on different member sockets. + + m_bTsbPdMode = true; + + m_tsTsbPdTimeBase = timebase; + m_bTsbPdWrapCheck = wrp; + m_tdTsbPdDelay = microseconds_from(delay); + m_DriftTracer.forceDrift(count_microseconds(udrift)); +} + +void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase, + bool wrp, + const steady_clock::duration& udrift) +{ + // This is only when a drift was updated on one of the group members. + HLOGC(brlog.Debug, + log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift) + << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase)); + + m_tsTsbPdTimeBase = timebase; + m_bTsbPdWrapCheck = wrp; + + m_DriftTracer.forceDrift(count_microseconds(udrift)); +} + +CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const +{ + const uint64_t carryover_us = + (m_bTsbPdWrapCheck && timestamp_us < TSBPD_WRAP_PERIOD) ? uint64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; + + return (m_tsTsbPdTimeBase + microseconds_from(carryover_us)); +} + +CTsbpdTime::time_point CTsbpdTime::getPktTsbPdTime(uint32_t usPktTimestamp) const +{ + return getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + microseconds_from(m_DriftTracer.drift()); +} + +CTsbpdTime::time_point CTsbpdTime::getPktTsbPdBaseTime(uint32_t usPktTimestamp) const +{ + return getTsbPdTimeBase(usPktTimestamp) + microseconds_from(usPktTimestamp); +} + +void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp) +{ + if (m_bTsbPdWrapCheck) + { + // Wrap check period. + if ((usPktTimestamp >= TSBPD_WRAP_PERIOD) && (usPktTimestamp <= (TSBPD_WRAP_PERIOD * 2))) + { + /* Exiting wrap check period (if for packet delivery head) */ + m_bTsbPdWrapCheck = false; + m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1); + LOGC(tslog.Debug, + log << "tsbpd wrap period ends with ts=" << usPktTimestamp << " - NEW TIME BASE: " + << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us"); + } + return; + } + + // Check if timestamp is in the last 30 seconds before reaching the MAX_TIMESTAMP. + if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) + { + // Approching wrap around point, start wrap check period (if for packet delivery head) + m_bTsbPdWrapCheck = true; + LOGC(tslog.Debug, + log << "tsbpd wrap period begins with ts=" << usPktTimestamp << " drift: " << m_DriftTracer.drift() + << "us."); + } +} + +void CTsbpdTime::getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const +{ + ScopedLock lck(m_mtxRW); + w_tb = m_tsTsbPdTimeBase; + w_udrift = microseconds_from(m_DriftTracer.drift()); + w_wrp = m_bTsbPdWrapCheck; +} + +} // namespace srt diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h new file mode 100644 index 000000000..5dcc7ff3a --- /dev/null +++ b/srtcore/tsbpd_time.h @@ -0,0 +1,161 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_TSBPD_TIME_H +#define INC_SRT_TSBPD_TIME_H + +#include "platform_sys.h" +#include "sync.h" +#include "utilities.h" + +namespace srt +{ + +/// @brief TimeStamp-Based Packet Delivery Mode (TSBPD) time conversion logic. +/// Used by the receiver to calculate delivery time of data packets. +/// See SRT RFC Section "Timestamp-Based Packet Delivery". +class CTsbpdTime +{ + typedef srt::sync::steady_clock steady_clock; + typedef steady_clock::time_point time_point; + typedef steady_clock::duration duration; + typedef srt::sync::Mutex Mutex; + +public: + CTsbpdTime() + : m_bTsbPdMode(false) + , m_tdTsbPdDelay(0) + , m_bTsbPdWrapCheck(false) + { + } + + /// Set TimeStamp-Based Packet Delivery Mode (receiver). + /// @param [in] timebase local time base (uSec) of packet time stamps including buffering delay. + /// @param [in] wrap wrapping period. + /// @param [in] delay negotiated TsbPD delay (buffering latency). + void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + + /// @brief Check if TSBPD logic is enabled. + /// @return true if TSBPD is enabled. + bool isEnabled() const { return m_bTsbPdMode; } + + /// @brief Apply new state derived from other members of a socket group. + /// @param timebase TSBPD base time. + /// @param wrp wrap period (enabled or not). + /// @param delay TSBPD delay. + /// @param udrift clock drift. + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); + + /// @brief Apply new clock state (TSBPD base and drift) derived from other members of a socket group. + /// @param timebase TSBPD base time. + /// @param wrp state of the wrapping period (enabled or disabled). + /// @param udrift clock drift. + void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); + + /// @brief Add new drift sample from an ACK-ACKACK pair. + /// ACKACK packets are sent immediately (except for UDP buffering). + /// + /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. + /// @param [out] w_udrift Current clock drift value. + /// @param [out] w_newtimebase Current TSBPD base time. + /// + /// @return true if TSBPD base time has changed, false otherwise. + bool addDriftSample(uint32_t pktTimestamp, + steady_clock::duration& w_udrift, + steady_clock::time_point& w_newtimebase); + + /// @brief Handle timestamp of data packet when 32-bit integer carryover is about to happen. + /// When packet timestamp approaches CPacket::MAX_TIMESTAMP, the TSBPD base time should be + /// shifted accordingly to correctly handle new packets with timestamps starting from zero. + /// @param usPktTimestamp timestamp field value of a data packet. + void updateTsbPdTimeBase(uint32_t usPktTimestamp); + + /// @brief Get TSBPD base time adjusted for carryover, which occurs when + /// a packet's timestamp exceeds the UINT32_MAX and continues from zero. + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return TSBPD base time for a provided packet timestamp. + time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; + + /// @brief Get packet TSBPD time without buffering delay and clock drift, which is + /// the target time for delivering the packet to an upstream application. + /// Essentially: getTsbPdTimeBase(usPktTimestamp) + usPktTimestamp + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return Packet TSBPD base time without buffering delay. + time_point getPktTsbPdBaseTime(uint32_t usPktTimestamp) const; + + /// @brief Get packet TSBPD time with buffering delay and clock drift, which is + /// the target time for delivering the packet to an upstream application + /// (including drift and carryover effects, if any). + /// Essentially: getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + drift() + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return Packet TSBPD time with buffering delay. + time_point getPktTsbPdTime(uint32_t usPktTimestamp) const; + + /// @brief Get current drift value. + /// @return current drift value. + int64_t drift() const { return m_DriftTracer.drift(); } + + /// @brief Get current overdrift value. + /// @return current overdrift value. + int64_t overdrift() const { return m_DriftTracer.overdrift(); } + + /// @brief Get internal state to apply to another member of a socket group. + /// @param w_tb TsbPd base time. + /// @param w_udrift drift value. + /// @param w_wrp wrap check. + void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const; + +private: + bool m_bTsbPdMode; //< Rreceiver buffering and TSBPD is active when true. + duration m_tdTsbPdDelay; //< Negotiated buffering delay. + + /// @brief Local time base for TsbPd. + /// @note m_tsTsbPdTimeBase is changed in the following cases: + /// 1. Initialized upon SRT_CMD_HSREQ packet as the difference with the current time: + /// = (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception. + /// 2. Shifted forward on timestamp overflow (@see CTsbpdTime::updateTsbPdTimeBase), when overflow + /// of the timestamp field value of a data packet is detected. + /// += CPacket::MAX_TIMESTAMP + 1 + /// 3. Clock drift (@see CTsbpdTime::addDriftSample, executed exclusively + /// from ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE + /// once the value of average drift exceeds this value in either direction. + /// += (+/-)TSBPD_DRIFT_MAX_VALUE + /// + /// @note The TSBPD base time is expected to hold the following condition: + /// (PACKET_TIMESTAMP + m_tsTsbPdTimeBase + drift) == NOW. + /// Then it can be used to estimate the origin time of a data packet, and calculate its delivery time + /// with buffering delay applied. + time_point m_tsTsbPdTimeBase; + + /// @note Packet timestamps wrap around every 01h11m35s (32-bit in usec). + /// A wrap check period starts 30 seconds (TSBPD_WRAP_PERIOD) before the wrap point. + /// During the wrap check period, packet timestamps smaller than 30 seconds + /// are considered to have been wrapped around. + /// The wrap check period ends 30 seconds after the wrap point, + /// after which the TSBPD base time is adjusted. + bool m_bTsbPdWrapCheck; // true: check packet time stamp wraparound (overflow). + static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) for timestamp wrapping period. + + /// Maximum clock drift (microseconds) above which TsbPD base time is already adjusted. + static const int TSBPD_DRIFT_MAX_VALUE = 5000; + /// Number of samples (ACKACK packets) on which to perform drift calculation and compensation. + static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; + DriftTracer m_DriftTracer; + + /// Protect simultaneous change of state (read/write). + mutable Mutex m_mtxRW; +}; + +} // namespace srt + +#endif // INC_SRT_TSBPD_TIME_H From 631d0fcb9ba7209e381a8f1927419545ef3d5c41 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 30 Apr 2021 14:56:31 +0200 Subject: [PATCH 159/790] [apps] Removed trailing comma in JSON stats (#1976) --- apps/apputil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index f772beadf..0333c768f 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -552,7 +552,7 @@ class SrtStatsJson : public SrtStatsWriter } // Close the general category entity - output << "}," << pretty_cr << endl; + output << "}" << pretty_cr << endl; return output.str(); } From d43b8366a1e5989a1f3dd784bca5d696cc3bbd77 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 3 May 2021 18:43:33 +0200 Subject: [PATCH 160/790] [docs] Fixed SRTO_RCVLATENCY description of the default value in live mode --- docs/API/API-socket-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 7f0316a6e..3c9f91556 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1172,7 +1172,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). The latency value in the receiving direction of the socket. This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. -**Default value**: 120 ms (depicted as 0) in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). +**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 From e33f0d3da62a39043cae41081bc2dcc69116e2d4 Mon Sep 17 00:00:00 2001 From: Alex Pokotilo Date: Tue, 4 May 2021 17:56:46 +1000 Subject: [PATCH 161/790] [docs] Added missing with-srt-name build opt description (#1982) --- docs/build/build-options.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 805214338..938d925d2 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -380,6 +380,10 @@ This should be the exact command used as a C compiler, possibly with version suffix, e.g. `clang-1.7.0`. If this option is used together with `--with-compiler-prefix`, its prefix will be added in front. +**`--with-srt-name=`** + +Overrides srt library name adding custom `` + **`--with-extralibs=`** This is an option required for unusual situations when a platform-specific From d9eefd8ae196c69b8df8b47b32ea361126dd9120 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 4 May 2021 12:57:03 +0200 Subject: [PATCH 162/790] [core] Fixed cookie contest (#1517) No longer relying on integer overflow. --- docs/features/handshake.md | 21 +++++++++++++++- srtcore/core.cpp | 50 ++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/docs/features/handshake.md b/docs/features/handshake.md index ef3fcfdbe..05af2cd28 100644 --- a/docs/features/handshake.md +++ b/docs/features/handshake.md @@ -697,7 +697,26 @@ connection will not be made until new, unique cookies are generated (after a delay of up to one minute). In the case of an application "connecting to itself", the cookies will always be identical, and so the connection will never be made. -When one party's cookie value is greater than its peer's, it wins the cookie +```c++ +// Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer. +// m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request. +const int64_t contest = int64_t(m_ConnReq.m_iCookie) - int64_t(m_ConnRes.m_iCookie); + +if ((contest & 0xFFFFFFFF) == 0) +{ + return HSD_DRAW; +} + +if (contest & 0x80000000) +{ + return HSD_RESPONDER; +} + +return HSD_INITIATOR; +``` + +When one party's cookie value is greater than its peer's (based on 32-bit subtraction of both +with potential overflow), it wins the cookie contest and becomes Initiator (the other party becomes the Responder). At this point there are two "handshake flows" possible (at least theoretically): diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c87efeb42..7fe156797 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3827,8 +3827,10 @@ void CUDT::cookieContest() if (m_SrtHsSide != HSD_DRAW) return; - HLOGC(cnlog.Debug, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + LOGC(cnlog.Error, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + // Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer. + // m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request. if (m_ConnReq.m_iCookie == 0 || m_ConnRes.m_iCookie == 0) { // Note that it's virtually impossible that Agent's cookie is not ready, this @@ -3841,31 +3843,43 @@ void CUDT::cookieContest() // // The cookie contest must be repeated every time because it // may change the state at some point. - int better_cookie = m_ConnReq.m_iCookie - m_ConnRes.m_iCookie; - - if (better_cookie > 0) - { - m_SrtHsSide = HSD_INITIATOR; + // + // In SRT v1.4.3 and prior the below subtraction was performed in 32-bit arithmetic. + // The result of subtraction can overflow 32-bits. + // Example + // m_ConnReq.m_iCookie = -1480577720; + // m_ConnRes.m_iCookie = 811599203; + // int64_t llBetterCookie = -1480577720 - 811599203 = -2292176923 (FFFF FFFF 7760 27E5); + // int32_t iBetterCookie = 2002790373 (7760 27E5); + // + // Now 64-bit arithmetic is used to calculate the actual result of subtraction. + // The 31-st bit is then checked to check if the resulting is negative in 32-bit aritmetics. + // This way the old contest behavior is preserved, and potential compiler optimisations are avoided. + const int64_t contest = int64_t(m_ConnReq.m_iCookie) - int64_t(m_ConnRes.m_iCookie); + + if ((contest & 0xFFFFFFFF) == 0) + { + // DRAW! The only way to continue would be to force the + // cookies to be regenerated and to start over. But it's + // not worth a shot - this is an extremely rare case. + // This can simply do reject so that it can be started again. + + // Pretend then that the cookie contest wasn't done so that + // it's done again. Cookies are baked every time anew, however + // the successful initial contest remains valid no matter how + // cookies will change. + + m_SrtHsSide = HSD_DRAW; return; } - if (better_cookie < 0) + if (contest & 0x80000000) { m_SrtHsSide = HSD_RESPONDER; return; } - // DRAW! The only way to continue would be to force the - // cookies to be regenerated and to start over. But it's - // not worth a shot - this is an extremely rare case. - // This can simply do reject so that it can be started again. - - // Pretend then that the cookie contest wasn't done so that - // it's done again. Cookies are baked every time anew, however - // the successful initial contest remains valid no matter how - // cookies will change. - - m_SrtHsSide = HSD_DRAW; + m_SrtHsSide = HSD_INITIATOR; } // This function should complete the data for KMX needed for an out-of-band From 97445fc9e553a47379f12dcb3c71b6b98ac91a2b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 5 May 2021 14:51:23 +0200 Subject: [PATCH 163/790] [core] Minor strerror_array_sizes refactoring --- srtcore/api.cpp | 4 +- srtcore/api.h | 4 +- srtcore/core.cpp | 10 ++--- srtcore/core.h | 2 +- srtcore/group_common.cpp | 80 +++++++++++++++++++-------------------- srtcore/group_common.h | 69 +++++++++++++++++---------------- srtcore/strerror_defs.cpp | 27 ++++++------- 7 files changed, 95 insertions(+), 101 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 71e4a8772..5bd307125 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -165,14 +165,14 @@ bool CUDTSocket::readReady() return broken(); } -bool CUDTSocket::writeReady() +bool CUDTSocket::writeReady() const { return (m_pUDT->m_bConnected && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) || broken(); } -bool CUDTSocket::broken() +bool CUDTSocket::broken() const { return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; } diff --git a/srtcore/api.h b/srtcore/api.h index 604f14a99..4ca299b24 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -182,8 +182,8 @@ class CUDTSocket // Instrumentally used by select() and also required for non-blocking // mode check in groups bool readReady(); - bool writeReady(); - bool broken(); + bool writeReady() const; + bool broken() const; private: CUDTSocket(const CUDTSocket&); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7fe156797..35db04142 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3115,7 +3115,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } - srt::groups::SocketData* f = m_parent->m_GroupMemberData; + groups::SocketData* f = m_parent->m_GroupMemberData; f->weight = link_weight; f->agent = m_parent->m_SelfAddr; @@ -3214,7 +3214,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l // Copy of addSocketToGroup. No idea how many parts could be common, not much. // Check if the socket already is in the group - srt::groups::SocketData* f; + groups::SocketData* f; if (gp->contains(m_SocketID, (f))) { // XXX This is internal error. Report it, but continue @@ -4664,7 +4664,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE"); - srt::groups::SocketData* gi = m_parent->m_GroupMemberData; + groups::SocketData* gi = m_parent->m_GroupMemberData; gi->sndstate = SRT_GST_IDLE; gi->rcvstate = SRT_GST_IDLE; gi->laststatus = SRTS_CONNECTED; @@ -9497,7 +9497,7 @@ int CUDT::processData(CUnit* in_unit) if (m_parent->m_GroupOf) { ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); - srt::groups::SocketData* gi = m_parent->m_GroupMemberData; + groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long @@ -9996,7 +9996,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) if (self->m_parent->m_GroupOf) { - srt::groups::SocketData* gi = self->m_parent->m_GroupMemberData; + groups::SocketData* gi = self->m_parent->m_GroupMemberData; if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); diff --git a/srtcore/core.h b/srtcore/core.h index dbb1ab5f9..2f97d9cfc 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1046,7 +1046,7 @@ class CUDT int64_t m_sndDurationTotal; // total real time for sending - time_point tsLastSampleTime; // last performance sample time + time_point tsLastSampleTime; // last performance sample time int64_t traceSent; // number of packets sent in the last trace interval int64_t traceSentUniq; // number of original packets sent in the last trace interval int64_t traceRecv; // number of packets received in the last trace interval diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp index 513e5065c..8716e343a 100644 --- a/srtcore/group_common.cpp +++ b/srtcore/group_common.cpp @@ -8,10 +8,10 @@ * */ - /***************************************************************************** - Written by - Haivision Systems Inc. - *****************************************************************************/ +/***************************************************************************** +Written by + Haivision Systems Inc. +*****************************************************************************/ #include "platform_sys.h" @@ -20,43 +20,43 @@ namespace srt { - namespace groups - { +namespace groups +{ - SocketData prepareSocketData(CUDTSocket* s) - { - // This uses default SRT_GST_BROKEN because when the group operation is done, - // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is - // recognized as an initial state of the fresh added socket to the group, - // so some "initial configuration" must be done on it, after which it's - // turned into SRT_GST_RUNNING, that is, it's treated as all others. When - // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned - // up, however, unless the status is simultaneously SRTS_BROKEN. +SocketData prepareSocketData(CUDTSocket* s) +{ + // This uses default SRT_GST_BROKEN because when the group operation is done, + // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is + // recognized as an initial state of the fresh added socket to the group, + // so some "initial configuration" must be done on it, after which it's + // turned into SRT_GST_RUNNING, that is, it's treated as all others. When + // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned + // up, however, unless the status is simultaneously SRTS_BROKEN. - // The order of operations is then: - // - add the socket to the group in this "broken" initial state - // - connect the socket (or get it extracted from accept) - // - update the socket state (should be SRTS_CONNECTED) - // - once the connection is established (may take time with connect), set SRT_GST_IDLE - // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING - SocketData sd = { - s->m_SocketID, - s, - -1, - SRTS_INIT, - SRT_GST_BROKEN, - SRT_GST_BROKEN, - -1, - -1, - sockaddr_any(), - sockaddr_any(), - false, - false, - false, - 0 // weight - }; - return sd; - } + // The order of operations is then: + // - add the socket to the group in this "broken" initial state + // - connect the socket (or get it extracted from accept) + // - update the socket state (should be SRTS_CONNECTED) + // - once the connection is established (may take time with connect), set SRT_GST_IDLE + // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING + SocketData sd = { + s->m_SocketID, + s, + -1, + SRTS_INIT, + SRT_GST_BROKEN, + SRT_GST_BROKEN, + -1, + -1, + sockaddr_any(), + sockaddr_any(), + false, + false, + false, + 0 // weight + }; + return sd; +} - } // namespace groups +} // namespace groups } // namespace srt diff --git a/srtcore/group_common.h b/srtcore/group_common.h index 13ef4ad4a..f6ac123c6 100644 --- a/srtcore/group_common.h +++ b/srtcore/group_common.h @@ -8,10 +8,10 @@ * */ - /***************************************************************************** - Written by - Haivision Systems Inc. - *****************************************************************************/ +/***************************************************************************** +Written by + Haivision Systems Inc. +*****************************************************************************/ #ifndef INC_SRT_GROUP_COMMON_H #define INC_SRT_GROUP_COMMON_H @@ -24,37 +24,36 @@ namespace srt { - namespace groups - { - typedef SRT_MEMBERSTATUS GroupState; - - struct SocketData - { - SRTSOCKET id; // same as ps->m_SocketID - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; - }; - - SocketData prepareSocketData(CUDTSocket* s); - - typedef std::list group_t; - typedef group_t::iterator gli_t; - - } // namespace groups -} // namespace srt +namespace groups +{ +typedef SRT_MEMBERSTATUS GroupState; +struct SocketData +{ + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; +}; + +SocketData prepareSocketData(CUDTSocket* s); + +typedef std::list group_t; +typedef group_t::iterator gli_t; + +} // namespace groups +} // namespace srt #endif // INC_SRT_GROUP_COMMON_H diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index ee32527bf..c37ee276d 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -119,27 +119,22 @@ const char** strerror_array_major [] = { strerror_msgs_again, // MJ_AGAIN = 6 strerror_msgs_peererror, // MJ_PEERERROR = 7 NULL - }; - - -size_t strerror_array_sizes [] = { - 1, - 6, - 3, - 4, - 5, - 15, - 5, - 1, +#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0]) + +const size_t strerror_array_sizes[] = { + SRT_ARRAY_SIZE(strerror_msgs_success) - 1, + SRT_ARRAY_SIZE(strerror_msgs_setup) - 1, + SRT_ARRAY_SIZE(strerror_msgs_connection) - 1, + SRT_ARRAY_SIZE(strerror_msgs_systemres) - 1, + SRT_ARRAY_SIZE(strerror_msgs_filesystem) - 1, + SRT_ARRAY_SIZE(strerror_msgs_notsup) - 1, + SRT_ARRAY_SIZE(strerror_msgs_again) - 1, + SRT_ARRAY_SIZE(strerror_msgs_peererror) - 1, 0 - }; - - - const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; From 41348e8dd0ab19e0a14264669f8b10145642e532 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 6 May 2021 00:21:57 +0800 Subject: [PATCH 164/790] [apps] Replace functions in UDT namespace by functions in srt namespace --- apps/srt-file-transmit.cpp | 8 ++++---- apps/srt-live-transmit.cpp | 2 +- apps/srt-tunnel.cpp | 8 ++++---- apps/transmitmedia.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 9c1ec82f4..d3ea92092 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -321,7 +321,7 @@ bool DoUpload(UriParser& ut, string path, string filename, << tar->GetSRTSocket() << endl; goto exit; } - UDT::setstreamid(tar->GetSRTSocket(), filename); + srt::setstreamid(tar->GetSRTSocket(), filename); } s = tar->GetSRTSocket(); @@ -539,7 +539,7 @@ bool DoDownload(UriParser& us, string directory, string filename, cerr << "Failed to add SRT client to poll" << endl; goto exit; } - id = UDT::getstreamid(s); + id = srt::getstreamid(s); cerr << "Source connected (listener), id [" << id << "]" << endl; connected = true; @@ -550,7 +550,7 @@ bool DoDownload(UriParser& us, string directory, string filename, { if (!connected) { - id = UDT::getstreamid(s); + id = srt::getstreamid(s); cerr << "Source connected (caller), id [" << id << "]" << endl; connected = true; @@ -714,7 +714,7 @@ int main(int argc, char** argv) } else { - UDT::setlogstream(logfile_stream); + srt::setlogstream(logfile_stream); } } diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index bfe4ff590..9aad3209c 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -434,7 +434,7 @@ int main(int argc, char** argv) } else { - UDT::setlogstream(logfile_stream); + srt::setlogstream(logfile_stream); } } diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index e5175ce8b..2c4aacfb3 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -1115,21 +1115,21 @@ int main( int argc, char** argv ) string loglevel = Option(params, "error", o_loglevel); string logfa = Option(params, "", o_logfa); srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel); - UDT::setloglevel(lev); + srt::setloglevel(lev); if (logfa == "") { - UDT::addlogfa(SRT_LOGFA_APP); + srt::addlogfa(SRT_LOGFA_APP); } else { // Add only selected FAs set unknown_fas; set fas = SrtParseLogFA(logfa, &unknown_fas); - UDT::resetlogfa(fas); + srt::resetlogfa(fas); // The general parser doesn't recognize the "app" FA, we check it here. if (unknown_fas.count("app")) - UDT::addlogfa(SRT_LOGFA_APP); + srt::addlogfa(SRT_LOGFA_APP); } string verbo = Option(params, "no", o_verbose); diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index fb4b13748..e5f76206d 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -624,7 +624,7 @@ void SrtModel::Establish(std::string& w_name) if (w_name != "") { Verb() << "Connect with requesting stream [" << w_name << "]"; - UDT::setstreamid(m_sock, w_name); + srt::setstreamid(m_sock, w_name); } else { @@ -667,7 +667,7 @@ void SrtModel::Establish(std::string& w_name) Verb() << "Accepting a client..."; AcceptNewClient(); // This rewrites m_sock with a new SRT socket ("accepted" socket) - w_name = UDT::getstreamid(m_sock); + w_name = srt::getstreamid(m_sock); Verb() << "... GOT CLIENT for stream [" << w_name << "]"; } } From 514f61e9e0d5dbc34dcd5da8f9353c9b7c459a35 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 7 May 2021 16:14:54 +0800 Subject: [PATCH 165/790] [core] Fix comments There is a typo in the comment, and specify the configuration value in comment has the risk of out of sync with source code. --- srtcore/socketconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 6b8a9325e..1db4bb63a 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -993,7 +993,7 @@ struct CSrtConfigSetter // File transfer mode: // - tsbpd: off // - latency: 0 - // - linger: 2 minutes (180s) + // - linger: on // - congctl: file (original UDT congestion control) // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) co.bTSBPD = false; From 88309439b5b71784f14730ecb5c651bbd968a68d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 11:24:38 +0200 Subject: [PATCH 166/790] [core] Improved 'no room to store' log message (#1909) --- srtcore/buffer.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++++ srtcore/buffer.h | 16 +++++++++++ srtcore/core.cpp | 11 ++++---- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 660f2b335..e2efcefff 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1722,6 +1722,75 @@ unsigned CRcvBuffer::getRcvAvgPayloadSize() const return m_uAvgPayloadSz; } +CRcvBuffer::ReadingState CRcvBuffer::debugGetReadingState() const +{ + ReadingState readstate; + + readstate.iNumAcknowledged = 0; + readstate.iNumUnacknowledged = m_iMaxPos; + + if ((NULL != m_pUnit[m_iStartPos]) && (m_pUnit[m_iStartPos]->m_iFlag == CUnit::GOOD)) + { + if (m_tsbpd.isEnabled()) + readstate.tsStart = m_tsbpd.getPktTsbPdTime(m_pUnit[m_iStartPos]->m_Packet.getMsgTimeStamp()); + + readstate.iNumAcknowledged = m_iLastAckPos > m_iStartPos + ? m_iLastAckPos - m_iStartPos + : m_iLastAckPos + (m_iSize - m_iStartPos); + } + + // All further stats are valid if TSBPD is enabled. + if (!m_tsbpd.isEnabled()) + return readstate; + + // m_iLastAckPos points to the first unacknowledged packet + const int iLastAckPos = (m_iLastAckPos - 1) % m_iSize; + if (m_iLastAckPos != m_iStartPos && (NULL != m_pUnit[iLastAckPos]) && (m_pUnit[iLastAckPos]->m_iFlag == CUnit::GOOD)) + { + readstate.tsLastAck = m_tsbpd.getPktTsbPdTime(m_pUnit[iLastAckPos]->m_Packet.getMsgTimeStamp()); + } + + const int iEndPos = (m_iLastAckPos + m_iMaxPos - 1) % m_iSize; + if (m_iMaxPos == 0) + { + readstate.tsEnd = readstate.tsLastAck; + } + else if ((NULL != m_pUnit[iEndPos]) && (m_pUnit[iEndPos]->m_iFlag == CUnit::GOOD)) + { + readstate.tsEnd = m_tsbpd.getPktTsbPdTime(m_pUnit[iEndPos]->m_Packet.getMsgTimeStamp()); + } + + return readstate; +} + +string CRcvBuffer::strFullnessState(const time_point& tsNow) const +{ + const ReadingState bufstate = debugGetReadingState(); + stringstream ss; + + ss << "Space avail " << getAvailBufSize() << "/" << m_iSize; + ss << " pkts. Packets ACKed: " << bufstate.iNumAcknowledged; + if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsLastAck)) + { + ss << " (TSBPD ready in "; + ss << count_milliseconds(bufstate.tsStart - tsNow); + ss << " : "; + ss << count_milliseconds(bufstate.tsLastAck - tsNow); + ss << " ms)"; + } + + ss << ", not ACKed: " << bufstate.iNumUnacknowledged; + if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsEnd)) + { + ss << ", timespan "; + ss << count_milliseconds(bufstate.tsEnd - bufstate.tsStart); + ss << " ms"; + } + + ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + return ss.str(); +} + void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) { for (int i = m_iStartPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 6a21df1ce..d32e1d29e 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -348,6 +348,21 @@ class CRcvBuffer /// @return size (bytes) of payload size unsigned getRcvAvgPayloadSize() const; + struct ReadingState + { + time_point tsStart; + time_point tsLastAck; + time_point tsEnd; + int iNumAcknowledged; + int iNumUnacknowledged; + }; + + ReadingState debugGetReadingState() const; + + /// Form a string of the current buffer fullness state. + /// number of packets acknowledged, TSBPD readiness, etc. + std::string strFullnessState(const time_point& tsNow) const; + /// Mark the message to be dropped from the message list. /// @param [in] msgno message number. /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the @@ -462,6 +477,7 @@ class CRcvBuffer bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: + /// @brief Get clock drift in microseconds. int64_t getDrift() const { return m_tsbpd.drift(); } public: diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 35db04142..81eaa9628 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9622,12 +9622,11 @@ int CUDT::processData(CUnit* in_unit) } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet: offset=" - << offset << " avail=" << avail_bufsize - << " ack.seq=" << m_iRcvLastSkipAck << " pkt.seq=" << rpkt.m_iSeqNo - << " rcv-remain=" << m_pRcvBuffer->debugGetSize() - << " drift=" << m_pRcvBuffer->getDrift() - ); + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(steady_clock::now()) + ); + return -1; } } From 2559797ed3284f53704ffbe57c90f47328a28669 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 15:06:35 +0200 Subject: [PATCH 167/790] [core] Main/backup: improved switching rules (#1857) and refactored the related code --- srtcore/core.cpp | 14 +- srtcore/core.h | 11 +- srtcore/filelist.maf | 2 + srtcore/group.cpp | 1322 ++++++++++++++++++-------------------- srtcore/group.h | 144 +++-- srtcore/group_backup.cpp | 159 +++++ srtcore/group_backup.h | 123 ++++ srtcore/group_common.cpp | 3 +- srtcore/group_common.h | 55 +- 9 files changed, 1036 insertions(+), 797 deletions(-) create mode 100644 srtcore/group_backup.cpp create mode 100644 srtcore/group_backup.h diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 81eaa9628..efc866164 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -248,6 +248,7 @@ void CUDT::construct() m_bClosing = false; m_bShutdown = false; m_bBroken = false; + m_bBreakAsUnstable = false; // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; @@ -950,12 +951,13 @@ void CUDT::open() m_tsLastRspAckTime = currtime; m_tsLastSndTime = currtime; - m_iReXmitCount = 1; - m_tsUnstableSince = steady_clock::time_point(); + m_tsUnstableSince = steady_clock::time_point(); m_tsFreshActivation = steady_clock::time_point(); + m_tsWarySince = steady_clock::time_point(); + + m_iReXmitCount = 1; m_iPktCount = 0; m_iLightACKCount = 1; - m_tsNextSendTime = steady_clock::time_point(); m_tdSendTimeDiff = microseconds_from(0); @@ -10777,15 +10779,15 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea next_exp_time = m_tsLastRspTime + exp_timeout; } - if (currtime <= next_exp_time) + if (currtime <= next_exp_time && !m_bBreakAsUnstable) return false; // ms -> us const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds - if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && - (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US))) + if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && + (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US)))) { // // Connection is broken. diff --git a/srtcore/core.h b/srtcore/core.h index 2f97d9cfc..97eda7436 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -420,6 +420,9 @@ class CUDT SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); + /// @brief Request a socket to be broken due to too long instability (normally by a group). + void breakAsUnstable() { m_bBreakAsUnstable = true; } + void ConnectSignal(ETransmissionEvent tev, EventSlot sl); void DisconnectSignal(ETransmissionEvent tev); @@ -726,6 +729,7 @@ class CUDT volatile bool m_bClosing; // If the UDT entity is closing volatile bool m_bShutdown; // If the peer side has shutdown the connection volatile bool m_bBroken; // If the connection has been broken + volatile bool m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. volatile bool m_bPeerHealth; // If the peer status is normal volatile int m_RejectReason; bool m_bOpened; // If the UDT entity has been opened @@ -908,7 +912,7 @@ class CUDT srt::sync::Mutex m_SendLock; // used to synchronize "send" call srt::sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) - srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics + mutable srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics void initSynch(); void destroySynch(); @@ -1090,8 +1094,9 @@ class CUDT static const int PACKETPAIR_MASK = 0xF; private: // Timers functions - time_point m_tsFreshActivation; // time of fresh activation of the link, or 0 if past the activation phase or idle - time_point m_tsUnstableSince; // time since unexpected ACK delay experienced, or 0 if link seems healthy + time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle + time_point m_tsUnstableSince; // GROUPS: time since unexpected ACK delay experienced, or 0 if link seems healthy + time_point m_tsWarySince; // GROUPS: time since an unstable link has first some response static const int BECAUSE_NO_REASON = 0, // NO BITS BECAUSE_ACK = 1 << 0, diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 400541499..54e15df62 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -29,6 +29,7 @@ window.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp +group_backup.cpp group_common.cpp SOURCES - !ENABLE_STDCXX_SYNC @@ -76,4 +77,5 @@ window.h PRIVATE HEADERS - ENABLE_EXPERIMENTAL_BONDING group.h +group_backup.h group_common.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d6dee49f9..b16bfc349 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -7,6 +7,7 @@ using namespace std; using namespace srt::sync; +using namespace srt::groups; using namespace srt_logging; // The SRT_DEF_VERSION is defined in core.cpp. @@ -636,7 +637,7 @@ inline int Value::fill(void* optval, int len, std::string value) if (size_t(len) < value.size()) return 0; memcpy(optval, value.c_str(), value.size()); - return value.size(); + return (int) value.size(); } template @@ -1716,7 +1717,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // is performed, and this one will result in none-write-ready, this will // be fixed just after returning from this function. - ready_again = ready_again | d->ps->writeReady(); + ready_again = ready_again || d->ps->writeReady(); } w_mc.grpdata_size = i; @@ -2811,23 +2812,42 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) } } -// For sorting group members by priority - -struct FPriorityOrder +/// @brief Compares group members by their weight (higher weight comes first). +struct FCompareByWeight { - // returned true = "elements are in the right order" - static bool check(uint16_t preceding, uint16_t succeeding) + typedef CUDTGroup::gli_t gli_t; + + /// @returns true if the first argument is less than (i.e. is ordered before) the second. + bool operator()(const gli_t preceding, const gli_t succeeding) { - return preceding > succeeding; + return preceding->weight > succeeding->weight; } +}; - typedef CUDTGroup::gli_t gli_t; +// [[using maybe_locked(this->m_GroupLock)]] +BackupMemberState CUDTGroup::sendBackup_QualifyIfStandBy(const gli_t d) +{ + if (!d->ps) + return BKUPST_BROKEN; - bool operator()(gli_t preceding, gli_t succeeding) + const SRT_SOCKSTATUS st = d->ps->getStatus(); + // If the socket is already broken, move it to broken. + if (int(st) >= int(SRTS_BROKEN)) { - return check(preceding->weight, succeeding->weight); + HLOGC(gslog.Debug, + log << "CUDTGroup::send.$" << id() << ": @" << d->id << " became " << SockStatusStr(st) + << ", WILL BE CLOSED."); + return BKUPST_BROKEN; } -}; + + if (st != SRTS_CONNECTED) + { + HLOGC(gslog.Debug, log << "CUDTGroup::send. @" << d->id << " is still " << SockStatusStr(st) << ", skipping."); + return BKUPST_PENDING; + } + + return BKUPST_STANDBY; +} // [[using maybe_locked(this->m_GroupLock)]] bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vector& w_pendingSockets) @@ -2855,12 +2875,75 @@ bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vecto return true; } -void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, - vector& w_wipeme, - vector& w_idleLinks, - vector& w_pendingSockets, - vector& w_unstableLinks, - vector& w_activeLinks) + +#if SRT_DEBUG_BONDING_STATES +class StabilityTracer +{ +public: + StabilityTracer() + { + } + + ~StabilityTracer() + { + srt::sync::ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint32_t activation_period_us, + int64_t stability_tmo_us, const std::string& state, uint16_t weight) + { + srt::sync::ScopedLock lck(m_mtx); + create_file(); + + m_fout << srt::sync::FormatTime(currtime) << ","; + m_fout << u.id() << ","; + m_fout << weight << ","; + m_fout << u.peerLatency_us() << ","; + m_fout << u.SRTT() << ","; + m_fout << u.RTTVar() << ","; + m_fout << stability_tmo_us << ","; + m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; + m_fout << state << ","; + m_fout << (srt::sync::is_zero(u.freshActivationStart()) ? -1 : (count_microseconds(currtime - u.freshActivationStart()))) << ","; + m_fout << activation_period_us << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + //srt::sync::ScopedLock lck(m_mtx); + m_fout << "Timepoint,SocketID,weight,usLatency,usRTT,usRTTVar,usStabilityTimeout,usSinceLastResp,State,usSinceActivation,usActivationPeriod\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "stability_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; +}; + +StabilityTracer s_stab_trace; +#endif + +void CUDTGroup::sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { // First, check status of every link - no matter if idle or active. for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) @@ -2887,36 +2970,39 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c HLOGC(gslog.Debug, log << "grp/sendBackup: socket in BROKEN state: @" << d->id << ", sockstatus=" << SockStatusStr(d->ps ? d->ps->getStatus() : SRTS_NONEXIST)); - w_wipeme.push_back(d->id); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_BROKEN, currtime); + w_sendBackupCtx.recordMemberState(&(*d), BKUPST_BROKEN); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(BKUPST_BROKEN), d->weight); +#endif continue; } if (d->sndstate == SRT_GST_IDLE) { - if (!send_CheckIdle(d, (w_wipeme), (w_pendingSockets))) - continue; + const BackupMemberState idle_state = sendBackup_QualifyIfStandBy(d); + sendBackup_AssignBackupState(d->ps->core(), idle_state, currtime); + w_sendBackupCtx.recordMemberState(&(*d), idle_state); - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket in IDLE state: @" << d->id << " - will activate it IF NEEDED"); - // This is idle, we'll take care of them next time - // Might be that: - // - this socket is idle, while some NEXT socket is running - // - we need at least one running socket to work BEFORE activating the idle one. - // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, - // and all others will be activated using the ISN from the first one. - w_idleLinks.push_back(d); - sendBackup_CheckIdleTime(d); + if (idle_state == BKUPST_STANDBY) + { + // TODO: Check if this is some abandoned logic. + sendBackup_CheckIdleTime(d); + } +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(idle_state), d->weight); +#endif continue; } if (d->sndstate == SRT_GST_RUNNING) { - if (!sendBackup_CheckRunningStability(d, (currtime))) - { - insert_uniq((w_unstableLinks), d); - } - // Unstable links should still be used for sending. - w_activeLinks.push_back(d); + const BackupMemberState active_state = sendBackup_QualifyActiveState(d, currtime); + sendBackup_AssignBackupState(d->ps->core(), active_state, currtime); + w_sendBackupCtx.recordMemberState(&(*d), active_state); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(active_state), d->weight); +#endif continue; } @@ -2924,7 +3010,56 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c log << "grp/sendBackup: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); - w_pendingSockets.push_back(d->id); + // Otherwise connection pending + sendBackup_AssignBackupState(d->ps->core(), BKUPST_PENDING, currtime); + w_sendBackupCtx.recordMemberState(&(*d), BKUPST_PENDING); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(BKUPST_PENDING), d->weight); +#endif + } +} + + +void CUDTGroup::sendBackup_AssignBackupState(CUDT& sock, BackupMemberState state, const steady_clock::time_point& currtime) +{ + switch (state) + { + case BKUPST_PENDING: + case BKUPST_STANDBY: + case BKUPST_BROKEN: + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_FRESH: + if (is_zero(sock.freshActivationStart())) + { + sock.m_tsFreshActivation = currtime; + } + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point();; + break; + case BKUPST_ACTIVE_STABLE: + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_UNSTABLE: + if (is_zero(sock.m_tsUnstableSince)) + { + sock.m_tsUnstableSince = currtime; + } + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_UNSTABLE_WARY: + if (is_zero(sock.m_tsWarySince)) + { + sock.m_tsWarySince = currtime; + } + break; + default: + break; } } @@ -2935,7 +3070,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // buffer gets empty so that we can make sure that KEEPALIVE will be the // really last sent for longer time. CUDT& u = w_d->ps->core(); - if (is_zero(u.m_tsFreshActivation)) + if (is_zero(u.m_tsFreshActivation)) // TODO: Check if this condition is ever false return; CSndBuffer* b = u.m_pSndBuffer; @@ -2955,255 +3090,133 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) } } -#if SRT_DEBUG_BONDING_STATES -class StabilityTracer +// [[using locked(this->m_GroupLock)]] +CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_t d, const time_point currtime) { -public: - StabilityTracer() - { - } - - ~StabilityTracer() - { - srt::sync::ScopedLock lck(m_mtx); - m_fout.close(); - } - - void trace(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint32_t activation_period_us, - int64_t stability_tmo_us, const std::string& state, uint16_t weight) - { - srt::sync::ScopedLock lck(m_mtx); - create_file(); - - m_fout << srt::sync::FormatTime(currtime) << ","; - m_fout << u.id() << ","; - m_fout << weight << ","; - m_fout << u.peerLatency_us() << ","; - m_fout << u.SRTT() << ","; - m_fout << u.RTTVar() << ","; - m_fout << stability_tmo_us << ","; - m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; - m_fout << state << ","; - m_fout << (srt::sync::is_zero(u.freshActivationStart()) ? -1 : (count_microseconds(currtime - u.freshActivationStart()))) << ","; - m_fout << activation_period_us << "\n"; - m_fout.flush(); - } - -private: - void print_header() - { - //srt::sync::ScopedLock lck(m_mtx); - m_fout << "Timepoint,SocketID,weight,usLatency,usRTT,usRTTVar,usStabilityTimeout,usSinceLastResp,State,usSinceActivation,usActivationPeriod\n"; - } - - void create_file() - { - if (m_fout) - return; - - std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); - str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part - while (str_tnow.find(':') != std::string::npos) { - str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); - } - const std::string fname = "stability_trace_" + str_tnow + ".csv"; - m_fout.open(fname, std::ofstream::out); - if (!m_fout) - std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + const CUDT& u = d->ps->core(); - print_header(); - } - -private: - srt::sync::Mutex m_mtx; - std::ofstream m_fout; -}; - -StabilityTracer s_stab_trace; -#endif - -/// TODO: Remove 'weight' parameter? Only needed for logging. -/// @retval 1 - link is identified as stable -/// @retval 0 - link state remains unchanged (too early to identify, still in activation phase) -/// @retval -1 - link is identified as unstable -static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint16_t weight ATR_UNUSED) -{ const uint32_t latency_us = u.peerLatency_us(); - const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. - const int64_t initial_stabtout_us = max(min_stability_us, latency_us); - const int64_t activation_period_us = initial_stabtout_us + 5 * CUDT::COMM_SYN_INTERVAL_US; + const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. + const int64_t initial_stabtout_us = max(min_stability_us, latency_us); + const int64_t probing_period_us = initial_stabtout_us + 5 * CUDT::COMM_SYN_INTERVAL_US; - // RTT and RTTVar values are still being refined during activation period, - // therefore the dymanic timeout should not be used in activation phase. + // RTT and RTTVar values are still being refined during the probing period, + // therefore the dymanic timeout should not be used during the probing period. const bool is_activation_phase = !is_zero(u.freshActivationStart()) - && (count_microseconds(currtime - u.freshActivationStart()) <= activation_period_us); + && (count_microseconds(currtime - u.freshActivationStart()) <= probing_period_us); + // Initial stability timeout is used only in activation phase. + // Otherwise runtime stability is used, including the WARY state. const int64_t stability_tout_us = is_activation_phase ? initial_stabtout_us // activation phase : min(max(min_stability_us, 2 * u.SRTT() + 4 * u.RTTVar()), latency_us); - + const steady_clock::time_point last_rsp = max(u.freshActivationStart(), u.lastRspTime()); const steady_clock::duration td_response = currtime - last_rsp; + + // No response for a long time if (count_microseconds(td_response) > stability_tout_us) { -#if SRT_DEBUG_BONDING_STATES - s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION-UNSTABLE" : "UNSTABLE", weight); -#endif - return -1; + return BKUPST_ACTIVE_UNSTABLE; } - // u.lastRspTime() > currtime is alwats true due to the very first check above in this function -#if SRT_DEBUG_BONDING_STATES - s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION" : "STABLE", weight); -#endif - return is_activation_phase ? 0 : 1; -} + enterCS(u.m_StatsLock); + const int64_t drop_total = u.m_stats.sndDropTotal; + leaveCS(u.m_StatsLock); + const bool have_new_drops = d->pktSndDropTotal != drop_total; + if (have_new_drops) + { + d->pktSndDropTotal = drop_total; + if (!is_activation_phase) + return BKUPST_ACTIVE_UNSTABLE; + } -// [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point currtime) -{ - CUDT& u = d->ps->core(); - // This link might be unstable, check its responsiveness status - // NOTE: currtime - last_rsp_time: we believe this value will be always positive as - // the Tk clock is believed to be monotonic. The resulting value - - // IMPORTANT: the socket could be potentially updated IN THE MEANTIME in another - // thread AFTER (!!!) currtime has been read, but BEFORE (!!!) this value us used - // for calculation - which could make the difference negative. - // There's no way to avoid it because it would require making a mutex-locking for - // updating the m_tsLastRspTime field. This is useless because avoiding the - // negative value is relatively easy, while introducing a mutex would only add a - // deadlock risk and performance degradation. + // Responsive: either stable, wary or still fresh activated. + if (is_activation_phase) + return BKUPST_ACTIVE_FRESH; - HLOGC(gslog.Debug, - log << "grp/sendBackup: CHECK STABLE: @" << d->id - << ": TIMEDIFF {response= " << FormatDuration(currtime - u.m_tsLastRspTime) - << " ACK=" << FormatDuration(currtime - u.m_tsLastRspAckTime) << " activation=" - << (!is_zero(u.m_tsFreshActivation) ? FormatDuration(currtime - u.m_tsFreshActivation) : "PAST") - << " unstable=" - << (!is_zero(u.m_tsUnstableSince) ? FormatDuration(currtime - u.m_tsUnstableSince) : "NEVER") - << "}"); + const bool is_wary = !is_zero(u.m_tsWarySince); + const bool is_wary_probing = is_wary + && (count_microseconds(currtime - u.m_tsWarySince) <= 4 * u.peerLatency_us()); - const int is_stable = sendBackup_CheckRunningLinkStable(u, currtime, d->weight); + const bool is_unstable = !is_zero(u.m_tsUnstableSince); - if (is_stable >= 0) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link STABLE: @" << d->id - << (!is_zero(u.m_tsUnstableSince) ? " - RESTORED" : " - CONTINUED") - << ", state RUNNING - will send a payload"); + // If unstable and not in wary, become wary. + if (is_unstable && !is_wary) + return BKUPST_ACTIVE_UNSTABLE_WARY; - u.m_tsUnstableSince = steady_clock::time_point(); + // Still probing for stability. + if (is_wary_probing) + return BKUPST_ACTIVE_UNSTABLE_WARY; - // For some cases - if (is_stable > 0) - u.m_tsFreshActivation = steady_clock::time_point(); - } - else + if (is_wary) { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link UNSTABLE for " << FormatDuration(currtime - u.m_tsUnstableSince) << " : @" - << d->id << " - will send a payload"); - if (is_zero(u.m_tsUnstableSince)) - { - u.m_tsUnstableSince = currtime; - } + LOGC(gslog.Debug, + log << "grp/sendBackup: @" << u.id() << " wary->stable after " << count_milliseconds(currtime - u.m_tsWarySince) << " ms"); } - return is_stable >= 0; + return BKUPST_ACTIVE_STABLE; } // [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckSendStatus(gli_t d, - const steady_clock::time_point& currtime ATR_UNUSED, - const int stat, - const int erc, - const int32_t lastseq, - const int32_t pktseq, - CUDT& w_u, - int32_t& w_curseq, - vector& w_parallel, - int& w_final_stat, - uint16_t& w_maxActiveWeight, - size_t& w_nsuccessful, - bool& w_is_unstable) +bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime ATR_UNUSED, + const int send_status, + const int32_t lastseq, + const int32_t pktseq, + CUDT& w_u, + int32_t& w_curseq, + int& w_final_stat) { - bool none_succeeded = true; + if (send_status == -1) + return false; // Sending failed. + - // sending over this socket has succeeded - if (stat != -1) + bool send_succeeded = false; + if (w_curseq == SRT_SEQNO_NONE) { - if (w_curseq == SRT_SEQNO_NONE) - { - w_curseq = pktseq; - } - else if (w_curseq != lastseq) - { - // We believe that all running links use the same seq. - // But we can do some sanity check. - LOGC(gslog.Error, - log << "grp/sendBackup: @" << w_u.m_SocketID << ": IPE: another running link seq discrepancy: %" - << lastseq << " vs. previous %" << w_curseq << " - fixing"); - - // Override must be done with a sequence number greater by one. - - // Example: - // - // Link 1 before sending: curr=1114, next=1115 - // After sending it reports pktseq=1115 - // - // Link 2 before sending: curr=1110, next=1111 (->lastseq before sending) - // THIS CHECK done after sending: - // -- w_curseq(1115) != lastseq(1111) - // - // NOW: Link 1 after sending is: - // curr=1115, next=1116 - // - // The value of w_curseq here = 1115, while overrideSndSeqNo - // calls setInitialSndSeq(seq), which sets: - // - curr = seq - 1 - // - next = seq - // - // So, in order to set curr=1115, next=1116 - // this must set to 1115+1. - - w_u.overrideSndSeqNo(CSeqNo::incseq(w_curseq)); - } + w_curseq = pktseq; + } + else if (w_curseq != lastseq) + { + // We believe that all active links use the same seq. + // But we can do some sanity check. + LOGC(gslog.Error, + log << "grp/sendBackup: @" << w_u.m_SocketID << ": IPE: another running link seq discrepancy: %" + << lastseq << " vs. previous %" << w_curseq << " - fixing"); - // If this link is already found as unstable, - // do not add it to the "w_parallel", as all links out - // of these "w_parallels" will be later tried to be - // shrunk to 1. Out of all links currently running we need - // only 1 link STABLE, and we allow any nymber of unstable - // links. + // Override must be done with a sequence number greater by one. - if (is_zero(w_u.m_tsUnstableSince)) - { - w_parallel.push_back(d); - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: Link @" << w_u.m_SocketID << " still UNSTABLE for " - << FormatDuration(currtime - w_u.m_tsUnstableSince) << ", not counting as w_parallel"); - } + // Example: + // + // Link 1 before sending: curr=1114, next=1115 + // After sending it reports pktseq=1115 + // + // Link 2 before sending: curr=1110, next=1111 (->lastseq before sending) + // THIS CHECK done after sending: + // -- w_curseq(1115) != lastseq(1111) + // + // NOW: Link 1 after sending is: + // curr=1115, next=1116 + // + // The value of w_curseq here = 1115, while overrideSndSeqNo + // calls setInitialSndSeq(seq), which sets: + // - curr = seq - 1 + // - next = seq + // + // So, in order to set curr=1115, next=1116 + // this must set to 1115+1. - // State it as succeeded, though. We don't know if the link - // is broken until we get the connection broken confirmation, - // and the instability state may wear off next time. - none_succeeded = false; - w_final_stat = stat; - ++w_nsuccessful; - w_maxActiveWeight = max(w_maxActiveWeight, d->weight); - } - else if (erc == SRT_EASYNCSND) - { - HLOGC(gslog.Debug, log << "grp/sendBackup: Link @" << w_u.m_SocketID << " DEEMED UNSTABLE (not ready to send)"); - w_is_unstable = true; + w_u.overrideSndSeqNo(CSeqNo::incseq(w_curseq)); } - return none_succeeded; + // State it as succeeded, though. We don't know if the link + // is broken until we get the connection broken confirmation, + // and the instability state may wear off next time. + send_succeeded = true; + w_final_stat = send_status; + + return send_succeeded; } // [[using locked(this->m_GroupLock)]] @@ -3257,85 +3270,64 @@ void CUDTGroup::sendBackup_Buffering(const char* buf, const int len, int32_t& w_ m_iLastSchedSeqNo = oldest_buffer_seq; } -bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idleLinks, - const vector& unstableLinks, - const vector& activeLinks, - const uint16_t maxActiveWeight, - string& activate_reason ATR_UNUSED) const +size_t CUDTGroup::sendBackup_TryActivateStandbyIfNeeded( + const char* buf, + const int len, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + int32_t& w_curseq, + int32_t& w_final_stat, + SendBackupCtx& w_sendBackupCtx, + CUDTException& w_cx, + const steady_clock::time_point& currtime) { - SRT_ASSERT(activeLinks.size() >= unstableLinks.size()); - bool need_activate = activeLinks.size() <= unstableLinks.size(); // <= for sanity, should be just == - IF_HEAVY_LOGGING(activate_reason = "BY NO REASON???"); - if (need_activate) + const unsigned num_standby = w_sendBackupCtx.countMembersByState(BKUPST_STANDBY); + if (num_standby == 0) { - HLOGC(gslog.Debug, - log << "grp/sendBackup: all " << activeLinks.size() << " links unstable - will activate an idle link"); - IF_HEAVY_LOGGING(activate_reason = "no stable links"); + return 0; } - else if (activeLinks.empty() || - (!idleLinks.empty() && idleLinks[0]->weight > maxActiveWeight)) + + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_FRESH); + const unsigned num_fresh = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + + if (num_stable + num_fresh == 0) { - need_activate = true; -#if ENABLE_HEAVY_LOGGING - if (activeLinks.empty()) - { - activate_reason = "no successful links found"; - LOGC(gslog.Debug, log << "grp/sendBackup: no active links were successful - will activate an idle link"); - } - else if (idleLinks.empty()) - { - // This should be impossible. - activate_reason = "WEIRD (no idle links!)"; - LOGC(gslog.Debug, - log << "grp/sendBackup: BY WEIRD AND IMPOSSIBLE REASON (IPE?) - will activate an idle link"); - } - else - { - LOGC(gslog.Debug, - log << "grp/sendBackup: found link weight " << idleLinks[0]->weight << " PREF OVER " - << maxActiveWeight << " (highest from active links) - will activate an idle link"); - activate_reason = "found higher weight link"; - } -#endif + LOGC(gslog.Warn, + log << "grp/sendBackup: trying to activate a stand-by link (" << num_standby << " available). " + << "Reason: no stable links" + ); + } + else if (w_sendBackupCtx.maxActiveWeight() < w_sendBackupCtx.maxStandbyWeight()) + { + LOGC(gslog.Warn, + log << "grp/sendBackup: trying to activate a stand-by link (" << num_standby << " available). " + << "Reason: max active weight " << w_sendBackupCtx.maxActiveWeight() + << ", max stand by weight " << w_sendBackupCtx.maxStandbyWeight() + ); } else { - HLOGC(gslog.Debug, - log << "grp/sendBackup: max active weight " << maxActiveWeight << ",: " - << " first idle wight: " << (idleLinks.size() > 0 ? int(idleLinks[0]->weight) : -1) - << " - will NOT activate an idle link"); + /*LOGC(gslog.Warn, + log << "grp/sendBackup: no need to activate (" << num_standby << " available). " + << "Max active weight " << w_sendBackupCtx.maxActiveWeight() + << ", max stand by weight " << w_sendBackupCtx.maxStandbyWeight() + );*/ + return 0; } - return need_activate; -} - -// [[using locked(this->m_GroupLock)]] -size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& idleLinks, - const char* buf, - const int len, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - int32_t& w_curseq, - int32_t& w_final_stat, - CUDTException& w_cx, - vector& w_parallel, - vector& w_wipeme, - const string& activate_reason ATR_UNUSED) -{ int stat = -1; - // If we have no stable links, activate one of idle links. + size_t num_activated = 0; - HLOGC(gslog.Debug, - log << "grp/sendBackup: " << activate_reason << ", trying to activate an idle link (" << idleLinks.size() - << " available)"); - - size_t nactive = 0; - - for (vector::const_iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) + w_sendBackupCtx.sortByWeightAndState(); + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) { + if (member->state != BKUPST_STANDBY) + continue; + int erc = 0; - gli_t d = *i; + SocketData* d = member->pSocketData; // Now send and check the status // The link could have got broken @@ -3350,7 +3342,7 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i // situation of sending the very first packet after connection. HLOGC(gslog.Debug, - log << "grp/sendBackup: ... trying @" << d->id << " - sending the VERY FIRST message"); + log << "grp/sendBackup: ... trying @" << d->id << " - sending the VERY FIRST message"); stat = d->ps->core().sendmsg2(buf, len, (w_mc)); if (stat != -1) @@ -3365,13 +3357,13 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i else { HLOGC(gslog.Debug, - log << "grp/sendBackup: ... trying @" << d->id << " - resending " << m_SenderBuffer.size() - << " collected messages..."); + log << "grp/sendBackup: ... trying @" << d->id << " - resending " << m_SenderBuffer.size() + << " collected messages..."); // Note: this will set the currently required packet // because it has been just freshly added to the sender buffer stat = sendBackupRexmit(d->ps->core(), (w_mc)); } - ++nactive; + ++num_activated; } catch (CUDTException& e) { @@ -3379,43 +3371,24 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i // but that's ok - we want this sending interrupted even in half. w_cx = e; stat = -1; - erc = e.getErrorCode(); + erc = e.getErrorCode(); } - d->sndresult = stat; + d->sndresult = stat; d->laststatus = d->ps->getStatus(); if (stat != -1) { - if (d->sndstate != SRT_GST_RUNNING) - { - steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsFreshActivation = currtime; - HLOGC(gslog.Debug, - log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno - << " LINK ACTIVATED (pri: " << d->weight << ")."); - } - else - { - LOGC(gslog.Warn, - log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno - << " LINK ACTIVATED (pri: " << d->weight << ")."); - } + d->sndstate = SRT_GST_RUNNING; + sendBackup_AssignBackupState(d->ps->core(), BKUPST_ACTIVE_FRESH, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_ACTIVE_FRESH); // Note: this will override the sequence number // for all next iterations in this loop. - d->sndstate = SRT_GST_RUNNING; - - if (is_zero(d->ps->core().m_tsUnstableSince)) - { - w_parallel.push_back(d); - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: Link @" << d->id << " (idle) UNSTABLE, not counting as parallel"); - } w_none_succeeded = false; - w_final_stat = stat; + w_final_stat = stat; + + LOGC(gslog.Warn, + log << "@" << d->id << " FRESH-ACTIVATED"); // We've activated the link, so that's enough. break; @@ -3426,29 +3399,30 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i if (erc != SRT_EASYNCSND) { isblocked = false; - w_wipeme.push_back(d->id); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_BROKEN, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_BROKEN); } // If we found a blocked link, leave it alone, however // still try to send something over another link - HLOGC(gslog.Debug, - log << "@" << d->id << " FAILED (" << (isblocked ? "blocked" : "ERROR") - << "), trying to activate another link."); + LOGC(gslog.Warn, + log << "@" << d->id << " FAILED (" << (isblocked ? "blocked" : "ERROR") + << "), trying to activate another link."); } - return nactive; + return num_activated; } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets, vector& w_wipeme) +void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { - if (pendingSockets.empty()) + if (w_sendBackupCtx.countMembersByState(BKUPST_PENDING) == 0) return; - HLOGC(gslog.Debug, log << "grp/send*: found pending sockets, polling them."); + HLOGC(gslog.Debug, log << "grp/send*: checking pending sockets."); - // These sockets if they are in pending state, they should be added to m_SndEID + // These sockets if they are in pending state, should be added to m_SndEID // at the connecting stage. CEPoll::fmap_t sready; @@ -3456,49 +3430,97 @@ void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets { // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); - copy(pendingSockets.begin(), pendingSockets.end(), back_inserter(w_wipeme)); + return; } - else + { - { - InvertedLock ug(m_GroupLock); - m_pGlobal->m_EPoll.swait( - *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened - } + InvertedLock ug(m_GroupLock); + m_pGlobal->m_EPoll.swait( + *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything has happened + } - if (m_bClosing) - { - HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } + if (m_bClosing) + { + HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } - // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + // Some sockets could have been closed in the meantime. + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_PENDING) + continue; + + const SRTSOCKET sockid = member->pSocketData->id; + if (!CEPoll::isready(sready, sockid, SRT_EPOLL_ERR)) + continue; + + HLOGC(gslog.Debug, log << "grp/send*: Socket @" << sockid << " reported FAILURE - qualifying as broken."); + w_sendBackupCtx.updateMemberState(member->pSocketData, BKUPST_BROKEN); + if (member->pSocketData->ps) + sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); + + const int no_events = 0; + m_pGlobal->m_EPoll.update_usock(m_SndEID, sockid, &no_events); + } + + // After that, all sockets that have been reported + // as ready to write should be removed from EID. This + // will also remove those sockets that have been added + // as redundant links at the connecting stage and became + // writable (connected) before this function had a chance + // to check them. + m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); +} + +// [[using locked(this->m_GroupLock)]] +void CUDTGroup::sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) +{ + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + if (num_stable == 0) + return; + + const unsigned num_unstable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE); + const unsigned num_wary = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE_WARY); + if (num_unstable + num_wary == 0) + return; + HLOGC(gslog.Debug, log << "grp/send*: checking unstable sockets."); - HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_ACTIVE_UNSTABLE && member->state != BKUPST_ACTIVE_UNSTABLE_WARY) + continue; - // sockets in EX: should be moved to w_wipeme. - for (vector::const_iterator i = pendingSockets.begin(); i != pendingSockets.end(); ++i) + CUDT& sock = member->pSocketData->ps->core(); + + if (is_zero(sock.m_tsUnstableSince)) { - if (CEPoll::isready(sready, *i, SRT_EPOLL_ERR)) - { - HLOGC(gslog.Debug, log << "grp/send*: Socket @" << (*i) << " reported FAILURE - moved to wiped."); - // Failed socket. Move d to w_wipeme. Remove from eid. - w_wipeme.push_back(*i); - int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); - } + LOGC(gslog.Error, log << "grp/send* IPE: Socket @" << member->socketID + << " is qualified as unstable, but does not have the 'unstable since' timestamp. Still marking for closure."); } - // After that, all sockets that have been reported - // as ready to write should be removed from EID. This - // will also remove those sockets that have been added - // as redundant links at the connecting stage and became - // writable (connected) before this function had a chance - // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); + const int unstable_for_ms = count_milliseconds(currtime - sock.m_tsUnstableSince); + if (unstable_for_ms < sock.peerIdleTimeout_ms()) + continue; + + // Requesting this socket to be broken with the next CUDT::checkExpTimer() call. + sock.breakAsUnstable(); + + LOGC(gslog.Warn, log << "grp/send*: Socket @" << member->socketID << " is unstable for " << unstable_for_ms + << "ms - requesting breakage."); + + //w_sendBackupCtx.updateMemberState(member->pSocketData, BKUPST_BROKEN); + //if (member->pSocketData->ps) + // sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); } } @@ -3538,6 +3560,45 @@ void CUDTGroup::send_CloseBrokenSockets(vector& w_wipeme) w_wipeme.clear(); } +// [[using locked(this->m_GroupLock)]] +void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) +{ + if (w_sendBackupCtx.countMembersByState(BKUPST_BROKEN) == 0) + return; + + InvertedLock ug(m_GroupLock); + + // With unlocked GroupLock, we can now lock GlobControlLock. + // This is needed prevent any of them be deleted from the container + // at the same time. + ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_BROKEN) + continue; + + // m_GroupLock is unlocked, therefore member->pSocketData can't be used. + const SRTSOCKET sockid = member->socketID; + CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(sockid); + + // If the socket has been just moved to ClosedSockets, it means that + // the object still exists, but it will be no longer findable. + if (!s) + continue; + + LOGC(gslog.Debug, + log << "grp/send...: BROKEN SOCKET @" << sockid << " - CLOSING, to be removed from group."); + + // As per sending, make it also broken so that scheduled + // packets will be also abandoned. + s->setBrokenClosed(); + } + + // TODO: all broken members are to be removed from the context now??? +} + struct FByOldestActive { typedef CUDTGroup::gli_t gli_t; @@ -3551,25 +3612,12 @@ struct FByOldestActive }; // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, - vector& w_parallel, +void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx, int& w_final_stat, bool& w_none_succeeded, SRT_MSGCTRL& w_mc, CUDTException& w_cx) { -#if ENABLE_HEAVY_LOGGING - // Potential problem to be checked in developer mode - for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) - { - if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) - { - LOGC(gslog.Debug, - log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); - } - } -#endif - // In contradiction to broadcast sending, backup sending must check // the blocking state in total first. We need this information through // epoll because we didn't use all sockets to send the data hence the @@ -3588,12 +3636,15 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // over any link (hence "none succeeded"), but there are some unstable // links and no parallel links. We need to WAIT for any of the links // to become available for sending. - if (!w_parallel.empty() || unstableLinks.empty() || !w_none_succeeded) - return; + // Note: A link is added in unstableLinks if sending has failed with SRT_ESYNCSND. + const unsigned num_unstable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE); + const unsigned num_wary = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE_WARY); + if ((num_unstable + num_wary == 0) || !w_none_succeeded) + return; - HLOGC(gslog.Debug, log << "grp/sendBackup: no parallel links and " - << unstableLinks.size() << " unstable links - checking..."); + HLOGC(gslog.Debug, log << "grp/sendBackup: no successfull sending: " + << (num_unstable + num_wary) << " unstable links - waiting to retry sending..."); // Note: GroupLock is set already, skip locks and checks getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); @@ -3630,7 +3681,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // Simply notify all dead links, regardless as to whether the number // of group members decreases below. If the number of corpses reaches // this number, consider the group connection broken. - size_t nlinks = m_Group.size(); + const size_t nlinks = m_Group.size(); size_t ndead = 0; RetryWaitBlocked: @@ -3714,8 +3765,13 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, int stat = -1; for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) { - // int erc = 0; - + // We are waiting only for active members + if (d->sndstate != SRT_GST_RUNNING) + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: member @" << d->id << " state is not RUNNING - SKIPPING from retry/waiting"); + continue; + } // Skip if not writable in this run if (!CEPoll::isready(sready, d->id, SRT_EPOLL_OUT)) { @@ -3724,13 +3780,6 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, continue; } - if (d->sndstate == SRT_GST_RUNNING) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link @" << d->id << " RUNNING - SKIPPING from activate and resend"); - continue; - } - try { // Note: this will set the currently required packet @@ -3744,7 +3793,6 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // but that's ok - we want this sending interrupted even in half. w_cx = e; stat = -1; - // erc = e.getErrorCode(); } d->sndresult = stat; @@ -3756,12 +3804,12 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, continue; } - w_parallel.push_back(d); w_final_stat = stat; - steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsFreshActivation = currtime; d->sndstate = SRT_GST_RUNNING; w_none_succeeded = false; + const steady_clock::time_point currtime = steady_clock::now(); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_ACTIVE_UNSTABLE_WARY, currtime); + w_sendBackupCtx.updateMemberState(&(*d), BKUPST_ACTIVE_UNSTABLE_WARY); HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); break; @@ -3775,76 +3823,76 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, HLOGC(gslog.Debug, log << "grp/sendBackup: still have " << nwaiting << " waiting and none succeeded, REPEAT"); goto RetryWaitBlocked; } - - HLOGC(gslog.Debug, log << "grp/sendBackup: " << nactivated << " links activated with " - << unstableLinks.size() << " unstable"); } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_SilenceRedundantLinks(vector& w_parallel) +void CUDTGroup::sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { // The most important principle is to keep the data being sent constantly, - // even if it means temporarily full redundancy. However, if you are certain - // that you have multiple stable links running at the moment, SILENCE all but - // the one with highest priority. - if (w_parallel.size() <= 1) + // even if it means temporarily full redundancy. + // A member can be silenced only if there is at least one stable memebr. + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + if (num_stable == 0) return; - sort(w_parallel.begin(), w_parallel.end(), FPriorityOrder()); - steady_clock::time_point currtime = steady_clock::now(); + // INPUT NEEDED: + // - stable member with maximum weight - vector::iterator b = w_parallel.begin(); + uint16_t max_weight_stable = 0; + SRTSOCKET stableSocketId = SRT_INVALID_SOCK; // SocketID of a stable link with higher weight + + w_sendBackupCtx.sortByWeightAndState(); + //LOGC(gslog.Debug, log << "grp/silenceRedundant: links after sort: " << w_sendBackupCtx.printMembers()); + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (!isStateActive(member->state)) + continue; - // Additional criterion: if you have multiple links with the same weight, - // check if you have at least one with m_tsTmpActiveSince == 0. If not, - // sort them additionally by this time. + const bool haveHigherWeightStable = stableSocketId != SRT_INVALID_SOCK; + const uint16_t weight = member->pSocketData->weight; - vector::iterator b1 = b, e = ++b1; + if (member->state == BKUPST_ACTIVE_STABLE) + { + // silence stable link if it is not the first stable + if (!haveHigherWeightStable) + { + max_weight_stable = (int) weight; + stableSocketId = member->socketID; + continue; + } + else + { + LOGC(gslog.Note, log << "grp/sendBackup: silencing stable member @" << member->socketID << " (weight " << weight + << ") in favor of @" << stableSocketId << " (weight " << max_weight_stable << ")"); + } + } + else if (haveHigherWeightStable && weight <= max_weight_stable) + { + LOGC(gslog.Note, log << "grp/sendBackup: silencing member @" << member->socketID << " (weight " << weight + << " " << stateToStr(member->state) + << ") in favor of @" << stableSocketId << " (weight " << max_weight_stable << ")"); + } + else + { + continue; + } - // Both e and b1 stand on b+1 position. - // We have a guarantee that b+1 still points to a valid element. - while (e != w_parallel.end()) - { - if ((*e)->weight != (*b)->weight) - break; - ++e; - } + // TODO: Move to a separate function sendBackup_SilenceMember + SocketData* d = member->pSocketData; + CUDT& u = d->ps->core(); - if (b1 != e) - { - // More than 1 link with the same weight. Sorting them according - // to a different criterion will not change the previous sorting order - // because the elements in this range are equal according to the previous - // criterion. - // Here find the link with least time. The "trap" zero time matches this - // requirement, occasionally. - sort(b, e, FByOldestActive()); - } + sendBackup_AssignBackupState(u, BKUPST_STANDBY, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_STANDBY); - // After finding the link to leave active, leave it behind. - HLOGC(gslog.Debug, log << "grp/sendBackup: keeping parallel link @" << (*b)->id << " and silencing others:"); - ++b; - for (; b != w_parallel.end(); ++b) - { - gli_t& d = *b; if (d->sndstate != SRT_GST_RUNNING) { LOGC(gslog.Error, - log << "grp/sendBackup: IPE: parallel link container contains non-running link @" << d->id); - continue; - } - CUDT& ce = d->ps->core(); - if (!is_zero(ce.m_tsFreshActivation) && sendBackup_CheckRunningLinkStable(ce, currtime, d->weight) != 1) - { - HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early"); + log << "grp/sendBackup: IPE: misidentified a non-running link @" << d->id << " as active"); continue; } - // Clear activation time because the link is no longer active! d->sndstate = SRT_GST_IDLE; - HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsFreshActivation)); - ce.m_tsFreshActivation = steady_clock::time_point(); } } @@ -3864,21 +3912,6 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // [[using assert(this->m_pSndBuffer != nullptr)]]; - // NOTE: This is a "vector of list iterators". Every element here - // is an iterator to another container. - // Note that "list" is THE ONLY container in standard C++ library, - // for which NO ITERATORS ARE INVALIDATED after a node at particular - // iterator has been removed, except for that iterator itself. - vector wipeme; - vector idleLinks; - vector pendingSockets; - vector activeLinks; // All non-idle and non-pending links - vector unstableLinks; // Active, but unstable. - - int stat = 0; - int final_stat = -1; - SRT_ATR_UNUSED CUDTException cx(MJ_SUCCESS, MN_NONE, 0); - // First, acquire GlobControlLock to make sure all member sockets still exist enterCS(m_pGlobal->m_GlobControlLock); ScopedLock guard(m_GroupLock); @@ -3895,130 +3928,46 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) steady_clock::time_point currtime = steady_clock::now(); - activeLinks.reserve(m_Group.size()); - - // Qualify states of member links - sendBackup_QualifyMemberStates(currtime, (wipeme), (idleLinks), (pendingSockets), (unstableLinks), (activeLinks)); - - // Sort the idle sockets by priority so the highest priority idle links are checked first. - sort(idleLinks.begin(), idleLinks.end(), FPriorityOrder()); - -#if ENABLE_HEAVY_LOGGING - { - vector show_running, show_idle; - for (vector::iterator i = activeLinks.begin(); i != activeLinks.end(); ++i) - show_running.push_back((*i)->id); - - for (vector::iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) - show_idle.push_back((*i)->id); - - LOGC(gslog.Debug, - log << "grp/sendBackup: RUNNING: " << PrintableMod(show_running, "@") - << " IDLE: " << PrintableMod(show_idle, "@")); - } -#endif - - // Ok, we've separated the unstable from activeLinks just to know if: - // - we have any STABLE activeLinks (if not, we must activate a backup link) - // - we have multiple stable activeLinks and we need to stop all but one - - // Normally there should be only one link with state == SRT_GST_RUNNING, but there might - // be multiple links set as running when a "breaking suspection" is set on a link. - - bool none_succeeded = true; // be pessimistic + SendBackupCtx sendBackupCtx; // default initialized as empty + // TODO: reserve? sendBackupCtx.memberStates.reserve(m_Group.size()); - // All sockets that are currently stable and sending was successful - // should be added to the vector. Later, all but the one with highest - // priority should remain active. - vector parallel; + sendBackup_QualifyMemberStates((sendBackupCtx), currtime); int32_t curseq = SRT_SEQNO_NONE; size_t nsuccessful = 0; - // Maximum weight of active links. - uint16_t maxActiveWeight = 0; - if (w_mc.srctime == 0) - w_mc.srctime = count_microseconds(steady_clock::now().time_since_epoch()); + SRT_ATR_UNUSED CUDTException cx(MJ_SUCCESS, MN_NONE, 0); // TODO: Delete then? + uint16_t maxActiveWeight = 0; // Maximum weight of active links. + // The number of bytes sent or -1 for error will be stored in group_send_result + int group_send_result = sendBackup_SendOverActive(buf, len, w_mc, currtime, (curseq), (nsuccessful), (maxActiveWeight), (sendBackupCtx), (cx)); + bool none_succeeded = (nsuccessful == 0); - // We believe that we need to send the payload over every activeLinks link anyway. - for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) - { - gli_t d = *snd; - int erc = 0; // success - // Remaining sndstate is SRT_GST_RUNNING. Send a payload through it. - CUDT& u = d->ps->core(); - int32_t lastseq = u.schedSeqNo(); - try - { - // This must be wrapped in try-catch because on error it throws an exception. - // Possible return values are only 0, in case when len was passed 0, or a positive - // >0 value that defines the size of the data that it has sent, that is, in case - // of Live mode, equal to 'len'. - stat = u.sendmsg2(buf, len, (w_mc)); - } - catch (CUDTException& e) - { - cx = e; - stat = -1; - erc = e.getErrorCode(); - } + // Save current payload in group's sender buffer. + sendBackup_Buffering(buf, len, (curseq), (w_mc)); - bool is_unstable = false; - none_succeeded &= sendBackup_CheckSendStatus(d, - currtime, - stat, - erc, - lastseq, - w_mc.pktseq, - (u), - (curseq), - (parallel), - (final_stat), - (maxActiveWeight), - (nsuccessful), - (is_unstable)); - - // TODO: Wasn't it done in sendBackup_QualifyMemberStates()? Sanity check? - if (is_unstable && is_zero(u.m_tsUnstableSince)) // Add to unstable only if it wasn't unstable already - insert_uniq((unstableLinks), d); + sendBackup_TryActivateStandbyIfNeeded(buf, len, (none_succeeded), + (w_mc), + (curseq), + (group_send_result), + (sendBackupCtx), + (cx), currtime); - d->sndresult = stat; - d->laststatus = d->ps->getStatus(); - } + sendBackup_CheckPendingSockets((sendBackupCtx), currtime); + sendBackup_CheckUnstableSockets((sendBackupCtx), currtime); - // Ok, we have attempted to send a payload over all active links - // We know that at least one is successful if we have non-default curmsgno - // value. + //LOGC(gslog.Debug, log << "grp/sendBackup: links after all checks: " << sendBackupCtx.printMembers()); - // Now we need to check the link that is currently defined as - // main active because we can have: - // - one active link only - we want to check its status - // - two active links - one "main active" and one "temporarily - // activated" + // Re-check after the waiting lock has been reacquired + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - // Here the only thing we need to decide about is: - // 1. if we have at least one active and STABLE link - // - if there are no stable links, activate one idle link - // 2. if we have more than one active and stable link - // - select those with highest priority out of them - // - select the first in order from those - // - silence the rest (turn them idle) + sendBackup_CloseBrokenSockets((sendBackupCtx)); - // In Backup group, we have the following possibilities - // - only one link active and stable (normal) - // - multiple links active (and possibly one of them stable) - // - // We might have had a situation that sending was not possible - // due to have been blocked. - // - // If you have any link blocked, treat it as unstable, which - // means that one link out of the waiting idle must be activated. - // - // HOWEVER: - // - // Collect blocked links in order to make an additional check: - // - // If all links out of the unstable-running links are blocked, + // Re-check after the waiting lock has been reacquired + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + // If all links out of the unstable-running links are blocked (SRT_EASYNCSND), // perform epoll wait on them. In this situation we know that // there are no idle blocked links because IDLE LINK CAN'T BE BLOCKED, // no matter what. It's because the link may only be blocked if @@ -4028,87 +3977,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // This means that in case when we have no stable links, we // need to try out any link that can accept the rexmit-load. // We'll check link stability at the next sending attempt. + sendBackup_RetryWaitBlocked((sendBackupCtx), (group_send_result), (none_succeeded), (w_mc), (cx)); - // Here we need to activate one IDLE link, if we have - // no stable links. - // Some portion of logical exclusions: - // - // - sockets that were broken in the beginning are already wiped out - // - broken sockets are checked first, so they can't be simultaneously idle - // - idle sockets can't get broken because there's no operation done on them - // - running sockets are the only one that could change sndstate here - // - running sockets can either remain running or turn to broken - // In short: Running and Broken sockets can't become idle, - // although Running sockets can become Broken. - - // There's no certainty here as to whether at least one link was - // running and it has successfully performed the operation. - // Might have even happened that we had 2 running links that - // got broken and 3 other links so far in idle sndstate that just connected - // at that very moment (in Backup group: 1 running stable, 1 running - // unstable, 3 links keeping connetion being idle). - - // In this case we have 3 idle links to activate one of, - // but there is no message number base. If so, take the number for - // the first activated link as a good deal. - // - // If it has additionally happened that the first link got broken at - // that very moment of sending, the second one has a chance to succeed - // and therefore take over the leading role in setting the leading message - // number. If the second one fails, too, then the only remaining idle link - // will simply go with its own original message number. - // - // Now we can go to the idle links and attempt to send the payload - // also over them. - - sendBackup_Buffering(buf, len, (curseq), (w_mc)); - - string activate_reason; - if (sendBackup_IsActivationNeeded(idleLinks, unstableLinks, activeLinks, maxActiveWeight, activate_reason)) - { - if (idleLinks.empty()) - { - HLOGP(gslog.Debug, "grp/sendBackup: no idle links to activate, keeping only unstable links"); - } - else - { - const size_t n ATR_UNUSED = sendBackup_TryActivateIdleLink(idleLinks, - buf, - len, - (none_succeeded), - (w_mc), - (curseq), - (final_stat), - (cx), - (parallel), - (wipeme), - activate_reason); - - HLOGC(gslog.Debug, log << "grp/sendBackup: activated " << n << " idle links tried"); - } - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: have activeLinks links, stable=" << (activeLinks.size() - unstableLinks.size()) - << " unstable=" << unstableLinks.size()); - } - - send_CheckPendingSockets(pendingSockets, (wipeme)); - - // Re-check after the waiting lock has been reacquired - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - send_CloseBrokenSockets((wipeme)); - - // Re-check after the waiting lock has been reacquired - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - sendBackup_RetryWaitBlocked(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); - - sendBackup_SilenceRedundantLinks((parallel)); + sendBackup_SilenceRedundantLinks((sendBackupCtx), currtime); // (closing condition checked inside this call) if (none_succeeded) @@ -4132,7 +4003,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // Note that list::size() is linear time, however this shouldn't matter, // as with the increased number of links in the redundancy group the // impossibility of using that many of them grows exponentally. - size_t grpsize = m_Group.size(); + const size_t grpsize = m_Group.size(); if (w_mc.grpdata_size < grpsize) { @@ -4169,9 +4040,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } HLOGC(gslog.Debug, - log << "grp/sendBackup: successfully sent " << final_stat << " bytes, " + log << "grp/sendBackup: successfully sent " << group_send_result << " bytes, " << (ready_again ? "READY for next" : "NOT READY to send next")); - return final_stat; + return group_send_result; } // [[using locked(this->m_GroupLock)]] @@ -4223,6 +4094,72 @@ int32_t CUDTGroup::addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& return m_SenderBuffer.front().mc.pktseq; } +int CUDTGroup::sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq, + size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx) +{ + if (w_mc.srctime == 0) + w_mc.srctime = count_microseconds(currtime.time_since_epoch()); + + SRT_ASSERT(w_nsuccessful == 0); + SRT_ASSERT(w_maxActiveWeight == 0); + + int group_send_result = SRT_ERROR; + + // TODO: implement iterator over active links + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (!isStateActive(member->state)) + continue; + + SocketData* d = member->pSocketData; + int erc = SRT_SUCCESS; + // Remaining sndstate is SRT_GST_RUNNING. Send a payload through it. + CUDT& u = d->ps->core(); + const int32_t lastseq = u.schedSeqNo(); + int sndresult = SRT_ERROR; + try + { + // This must be wrapped in try-catch because on error it throws an exception. + // Possible return values are only 0, in case when len was passed 0, or a positive + // >0 value that defines the size of the data that it has sent, that is, in case + // of Live mode, equal to 'len'. + sndresult = u.sendmsg2(buf, len, (w_mc)); + } + catch (CUDTException& e) + { + w_cx = e; + erc = e.getErrorCode(); + sndresult = SRT_ERROR; + } + + const bool send_succeeded = sendBackup_CheckSendStatus( + currtime, + sndresult, + lastseq, + w_mc.pktseq, + (u), + (w_curseq), + (group_send_result)); + + if (send_succeeded) + { + ++w_nsuccessful; + w_maxActiveWeight = max(w_maxActiveWeight, d->weight); + } + else if (erc == SRT_EASYNCSND) + { + sendBackup_AssignBackupState(u, BKUPST_ACTIVE_UNSTABLE, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_ACTIVE_UNSTABLE); + } + + d->sndresult = sndresult; + d->laststatus = d->ps->getStatus(); + } + + return group_send_result; +} + // [[using locked(this->m_GroupLock)]] int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { @@ -4252,7 +4189,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) size_t skip_initial = 0; if (curseq != core.schedSeqNo()) { - int distance = CSeqNo::seqoff(core.schedSeqNo(), curseq); + const int distance = CSeqNo::seqoff(core.schedSeqNo(), curseq); if (distance < 0) { // This may happen in case when the link to be activated is already running. @@ -4261,8 +4198,8 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // packets that are in the past towards the scheduling sequence. skip_initial = -distance; LOGC(gslog.Warn, - log << "sendBackupRexmit: OVERRIDE attempt to %" << core.schedSeqNo() << " from BACKWARD %" << curseq - << " - DENIED; skip " << skip_initial << " packets"); + log << "sendBackupRexmit: OVERRIDE attempt. Link seqno %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq + << " - DENIED; skip " << skip_initial << " pkts, " << m_SenderBuffer.size() << " pkts in buffer"); } else { @@ -4279,12 +4216,16 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) } } - senderBuffer_t::iterator i = m_SenderBuffer.begin(); if (skip_initial >= m_SenderBuffer.size()) + { + LOGC(gslog.Warn, + log << "sendBackupRexmit: All packets were skipped. Nothing to send %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq + << " - DENIED; skip " << skip_initial << " packets"); return 0; // can't return any other state, nothing was sent - else if (skip_initial) - i += skip_initial; + } + + senderBuffer_t::iterator i = m_SenderBuffer.begin() + skip_initial; // Send everything - including the packet freshly added to the buffer for (; i != m_SenderBuffer.end(); ++i) @@ -4422,7 +4363,8 @@ int CUDTGroup::configure(const char* str) } break;*/ - + case SRT_GTYPE_BROADCAST: + case SRT_GTYPE_BACKUP: default: if (config == "") { diff --git a/srtcore/group.h b/srtcore/group.h index 1477a4583..ad779e39f 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -20,6 +20,7 @@ Written by #include "common.h" #include "packet.h" #include "group_common.h" +#include "group_backup.h" #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; @@ -32,7 +33,9 @@ class CUDTGroup typedef srt::sync::steady_clock::time_point time_point; typedef srt::sync::steady_clock::duration duration; typedef srt::sync::steady_clock steady_clock; - typedef srt::groups::SocketData SocketData; + typedef srt::groups::SocketData SocketData; + typedef srt::groups::SendBackupCtx SendBackupCtx; + typedef srt::groups::BackupMemberState BackupMemberState; public: typedef SRT_MEMBERSTATUS GroupState; @@ -211,102 +214,101 @@ class CUDTGroup int sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc); // Support functions for sendBackup and sendBroadcast + /// Check if group member is idle. + /// @param d group member + /// @param[in,out] w_wipeme array of sockets to remove from group + /// @param[in,out] w_pendingLinks array of sockets pending for connection + /// @returns true if d is idle (standby), false otherwise bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pendingLinks); + + + /// This function checks if the member has just become idle (check if sender buffer is empty) to send a KEEPALIVE immidiatelly. + /// @todo Check it is some abandoned logic. void sendBackup_CheckIdleTime(gli_t w_d); /// Qualify states of member links. /// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]] + /// @param[out] w_sendBackupCtx the context will be updated with state qualifications /// @param[in] currtime current timestamp - /// @param[out] w_wipeme broken links or links about to be closed - /// @param[out] w_idleLinks idle links (connected, but not used for transmission) - /// @param[out] w_pendingSockets sockets pending to be connected - /// @param[out] w_unstableLinks active member links qualified as unstable - /// @param[out] w_activeLinks all active member links, including unstable - void sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, - std::vector& w_wipeme, - std::vector& w_idleLinks, - std::vector& w_pendingSockets, - std::vector& w_unstableLinks, - std::vector& w_activeLinks); - - /// Check if a running link is stable. - /// @retval true running link is stable - /// @retval false running link is unstable - bool sendBackup_CheckRunningStability(const gli_t d, const time_point currtime); + void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + void sendBackup_AssignBackupState(CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); + + /// Qualify the state of the active link: fresh, stable, unstable, wary. + /// @retval active backup member state: fresh, stable, unstable, wary. + BackupMemberState sendBackup_QualifyActiveState(const gli_t d, const time_point currtime); + + BackupMemberState sendBackup_QualifyIfStandBy(const gli_t d); + + /// Sends the same payload over all active members. + /// @param[in] buf payload + /// @param[in] len payload length in bytes + /// @param[in,out] w_mc message control + /// @param[in] currtime current time + /// @param[in] currseq current packet sequence number + /// @param[out] w_nsuccessful number of members with successfull sending. + /// @param[in,out] maxActiveWeight + /// @param[in,out] sendBackupCtx context + /// @param[in,out] w_cx error + /// @return group send result: -1 if sending over all members has failed; number of bytes sent overwise. + int sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq, + size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx); /// Check link sending status - /// @param[in] d Group member iterator /// @param[in] currtime Current time (logging only) - /// @param[in] stat Result of sending over the socket + /// @param[in] send_status Result of sending over the socket /// @param[in] lastseq Last sent sequence number before the current sending operation /// @param[in] pktseq Packet sequence number currently tried to be sent /// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo) /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) - /// @param[out] w_parallel Parallel link container (will be filled inside this function) - /// @param[out] w_final_stat Status to be reported by this function eventually - /// @param[out] w_maxActiveWeight Maximum weight value of active links - /// @param[out] w_nsuccessful Updates the number of successful links - /// @param[out] w_is_unstable Set true if sending resulted in AGAIN error. + /// @param[out] w_final_stat w_final_stat = send_status if sending succeded. /// /// @returns true if the sending operation result (submitted in stat) is a success, false otherwise. - bool sendBackup_CheckSendStatus(const gli_t d, - const time_point& currtime, - const int stat, - const int erc, + bool sendBackup_CheckSendStatus(const time_point& currtime, + const int send_status, const int32_t lastseq, const int32_t pktseq, CUDT& w_u, int32_t& w_curseq, - std::vector& w_parallel, - int& w_final_stat, - uint16_t& w_maxActiveWeight, - size_t& w_nsuccessful, - bool& w_is_unstable); + int& w_final_stat); void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc); - /// Check activation conditions and activate a backup link if needed. - /// Backup link activation is needed if: - /// - /// 1. All currently active links are unstable. - /// Note that unstable links still count as sendable; they - /// are simply links that were qualified for sending, but: - /// - have exceeded response timeout - /// - have hit EASYNCSND error during sending - /// - /// 2. Another reason to activate might be if one of idle links - /// has a higher weight than any link currently active - /// (those are collected in 'sendable_pri'). - /// If there are no sendable, a new link needs to be activated anyway. - bool sendBackup_IsActivationNeeded(const std::vector& idleLinks, - const std::vector& unstable, - const std::vector& sendable, - const uint16_t max_sendable_weight, - std::string& activate_reason) const; - - size_t sendBackup_TryActivateIdleLink(const std::vector& idleLinks, - const char* buf, - const int len, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - int32_t& w_curseq, - int32_t& w_final_stat, - CUDTException& w_cx, - std::vector& w_parallel, - std::vector& w_wipeme, - const std::string& activate_reason); - - /// Check if pending sockets are to be closed. - /// @param[in] pending pending sockets - /// @param[in,out] w_wipeme a list of sockets to be removed from the group - void send_CheckPendingSockets(const std::vector& pending, std::vector& w_wipeme); + size_t sendBackup_TryActivateStandbyIfNeeded( + const char* buf, + const int len, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + int32_t& w_curseq, + int32_t& w_final_stat, + SendBackupCtx& w_sendBackupCtx, + CUDTException& w_cx, + const steady_clock::time_point& currtime); + + /// Check if pending sockets are to be qualified as broken. + /// This qualification later results in removing the socket from a group and closing it. + /// @param[in,out] a context with a list of member sockets, some pending might qualified broken + void sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + /// Check if unstable sockets are to be qualified as broken. + /// The main reason for such qualification is if a socket is unstable for too long. + /// This qualification later results in removing the socket from a group and closing it. + /// @param[in,out] a context with a list of member sockets, some pending might qualified broken + void sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + /// @brief Marks broken sockets as closed. Used in broadcast sending. + /// @param w_wipeme a list of sockets to close void send_CloseBrokenSockets(std::vector& w_wipeme); - void sendBackup_RetryWaitBlocked(const std::vector& unstable, - std::vector& w_parallel, + + /// @brief Marks broken sockets as closed. Used in backup sending. + /// @param w_sendBackupCtx the context with a list of broken sockets + void sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx); + + void sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx, int& w_final_stat, bool& w_none_succeeded, SRT_MSGCTRL& w_mc, CUDTException& w_cx); - void sendBackup_SilenceRedundantLinks(std::vector& w_parallel); + void sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); void send_CheckValidSockets(); diff --git a/srtcore/group_backup.cpp b/srtcore/group_backup.cpp new file mode 100644 index 000000000..9adb19607 --- /dev/null +++ b/srtcore/group_backup.cpp @@ -0,0 +1,159 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#include "platform_sys.h" +#include +#include + +#include "group_backup.h" + + +namespace srt +{ +namespace groups +{ + +using namespace std; +using namespace srt_logging; + +const char* stateToStr(BackupMemberState state) +{ + switch (state) + { + case srt::groups::BKUPST_UNKNOWN: + return "UNKNOWN"; + case srt::groups::BKUPST_PENDING: + return "PENDING"; + case srt::groups::BKUPST_STANDBY: + return "STANDBY"; + case srt::groups::BKUPST_ACTIVE_FRESH: + return "ACTIVE_FRESH"; + case srt::groups::BKUPST_ACTIVE_STABLE: + return "ACTIVE_STABLE"; + case srt::groups::BKUPST_ACTIVE_UNSTABLE: + return "ACTIVE_UNSTABLE"; + case srt::groups::BKUPST_ACTIVE_UNSTABLE_WARY: + return "ACTIVE_UNSTABLE_WARY"; + case srt::groups::BKUPST_BROKEN: + return "BROKEN"; + default: + break; + } + + return "WRONG_STATE"; +} + +/// @brief Compares group members by their weight (higher weight comes first), then state. +/// Higher weight comes first, same weight: stable, then fresh active. +struct FCompareByWeight +{ + /// @returns true if the first argument is less than (i.e. is ordered before) the second. + bool operator()(const BackupMemberStateEntry& a, const BackupMemberStateEntry& b) + { + if (a.pSocketData != NULL && b.pSocketData != NULL + && (a.pSocketData->weight != b.pSocketData->weight)) + return a.pSocketData->weight > b.pSocketData->weight; + + if (a.state != b.state) + { + SRT_STATIC_ASSERT(BKUPST_ACTIVE_STABLE > BKUPST_ACTIVE_FRESH, "Wrong ordering"); + return a.state > b.state; + } + + // the order does not matter, but comparator must return a different value for not equal a and b + return a.socketID < b.socketID; + } +}; + +void SendBackupCtx::recordMemberState(SocketData* pSockData, BackupMemberState st) +{ + m_memberStates.push_back(BackupMemberStateEntry(pSockData, st)); + ++m_stateCounter[st]; + + if (st == BKUPST_STANDBY) + { + m_standbyMaxWeight = max(m_standbyMaxWeight, pSockData->weight); + } + else if (isStateActive(st)) + { + m_activeMaxWeight = max(m_activeMaxWeight, pSockData->weight); + } +} + +void SendBackupCtx::updateMemberState(const SocketData* pSockData, BackupMemberState st) +{ + typedef vector::iterator iter_t; + for (iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + if (i->pSocketData == NULL) + continue; + + if (i->pSocketData != pSockData) + continue; + + if (i->state == st) + return; + + --m_stateCounter[i->state]; + ++m_stateCounter[st]; + i->state = st; + + return; + } + + + LOGC(gslog.Error, + log << "IPE: SendBackupCtx::updateMemberState failed to locate member"); +} + +void SendBackupCtx::sortByWeightAndState() +{ + sort(m_memberStates.begin(), m_memberStates.end(), FCompareByWeight()); +} + +BackupMemberState SendBackupCtx::getMemberState(const SocketData* pSockData) const +{ + typedef vector::const_iterator const_iter_t; + for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + if (i->pSocketData != pSockData) + continue; + + return i->state; + } + + // The entry was not found + // TODO: Maybe throw an exception here? + return BKUPST_UNKNOWN; +} + +unsigned SendBackupCtx::countMembersByState(BackupMemberState st) const +{ + return m_stateCounter[st]; +} + +std::string SendBackupCtx::printMembers() const +{ + stringstream ss; + typedef vector::const_iterator const_iter_t; + for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + ss << "@" << i->socketID << " w " << i->pSocketData->weight << " state " << stateToStr(i->state) << ", "; + } + return ss.str(); +} + +} // namespace groups +} // namespace srt diff --git a/srtcore/group_backup.h b/srtcore/group_backup.h new file mode 100644 index 000000000..760ccfbe4 --- /dev/null +++ b/srtcore/group_backup.h @@ -0,0 +1,123 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#ifndef INC_SRT_GROUP_BACKUP_H +#define INC_SRT_GROUP_BACKUP_H + +#include "srt.h" +#include "common.h" +#include "group_common.h" + +#include + +namespace srt +{ +namespace groups +{ + enum BackupMemberState + { + BKUPST_UNKNOWN = -1, + + BKUPST_PENDING = 0, + BKUPST_STANDBY = 1, + BKUPST_BROKEN = 2, + + BKUPST_ACTIVE_UNSTABLE = 3, + BKUPST_ACTIVE_UNSTABLE_WARY = 4, + BKUPST_ACTIVE_FRESH = 5, + BKUPST_ACTIVE_STABLE = 6, + + BKUPST_E_SIZE = 7 + }; + + const char* stateToStr(BackupMemberState state); + + inline bool isStateActive(BackupMemberState state) + { + if (state == BKUPST_ACTIVE_FRESH + || state == BKUPST_ACTIVE_STABLE + || state == BKUPST_ACTIVE_UNSTABLE + || state == BKUPST_ACTIVE_UNSTABLE_WARY) + { + return true; + } + + return false; + } + + struct BackupMemberStateEntry + { + BackupMemberStateEntry(SocketData* psock, BackupMemberState st) + : pSocketData(psock) + , socketID(psock->id) + , state(st) + {} + + SocketData* pSocketData; // accessing pSocketDataIt requires m_GroupLock + SRTSOCKET socketID; // therefore socketID is saved separately (needed to close broken sockets) + BackupMemberState state; + }; + + /// @brief A context needed for main/backup sending function. + /// @todo Using gli_t here does not allow to safely store the context outside of the sendBackup calls. + class SendBackupCtx + { + public: + SendBackupCtx() + : m_stateCounter() // default init with zeros + , m_activeMaxWeight() + , m_standbyMaxWeight() + { + } + + /// @brief Adds or updates a record of the member socket state. + /// @param pSocketDataIt Iterator to a socket + /// @param st State of the memmber socket + /// @todo Implement updating member state + void recordMemberState(SocketData* pSocketDataIt, BackupMemberState st); + + /// @brief Updates a record of the member socket state. + /// @param pSocketDataIt Iterator to a socket + /// @param st State of the memmber socket + /// @todo To be replaced by recordMemberState + /// @todo Update max weights? + void updateMemberState(const SocketData* pSocketDataIt, BackupMemberState st); + + /// @brief sorts members in order + /// Higher weight comes first, same weight: stable first, then fresh active. + void sortByWeightAndState(); + + BackupMemberState getMemberState(const SocketData* pSocketDataIt) const; + + unsigned countMembersByState(BackupMemberState st) const; + + const std::vector& memberStates() const { return m_memberStates; } + + uint16_t maxStandbyWeight() const { return m_standbyMaxWeight; } + uint16_t maxActiveWeight() const { return m_activeMaxWeight; } + + std::string printMembers() const; + + private: + std::vector m_memberStates; // TODO: consider std::map here? + unsigned m_stateCounter[BKUPST_E_SIZE]; + uint16_t m_activeMaxWeight; + uint16_t m_standbyMaxWeight; + }; + +} // namespace groups +} // namespace srt + +#endif // INC_SRT_GROUP_BACKUP_H diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp index 8716e343a..536bdf52c 100644 --- a/srtcore/group_common.cpp +++ b/srtcore/group_common.cpp @@ -53,7 +53,8 @@ SocketData prepareSocketData(CUDTSocket* s) false, false, false, - 0 // weight + 0, // weight + 0 // pktSndDropTotal }; return sd; } diff --git a/srtcore/group_common.h b/srtcore/group_common.h index f6ac123c6..d780d0b9a 100644 --- a/srtcore/group_common.h +++ b/srtcore/group_common.h @@ -26,32 +26,35 @@ namespace srt { namespace groups { -typedef SRT_MEMBERSTATUS GroupState; - -struct SocketData -{ - SRTSOCKET id; // same as ps->m_SocketID - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; -}; - -SocketData prepareSocketData(CUDTSocket* s); - -typedef std::list group_t; -typedef group_t::iterator gli_t; + typedef SRT_MEMBERSTATUS GroupState; + + struct SocketData + { + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; + + // Stats + int64_t pktSndDropTotal; + }; + + SocketData prepareSocketData(CUDTSocket* s); + + typedef std::list group_t; + typedef group_t::iterator gli_t; } // namespace groups } // namespace srt From 14225721a1e16710701bb6d2ce89b9f962172509 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 15:09:49 +0200 Subject: [PATCH 168/790] [docs] Added Main/Backup mode overview (#1989) --- docs/README.md | 34 ++-- docs/features/bonding-main-backup.md | 248 +++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 docs/features/bonding-main-backup.md diff --git a/docs/README.md b/docs/README.md index 2d91117d7..3e5ce72c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,5 @@ # Documentation Overview - - ## SRT API Documents | Document Title | Folder | File Name | Description | @@ -40,28 +33,37 @@ | Document Title | Folder | File Name | Description | | :----------------------------------------------------------- | :---------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| [SRT Access Control](features/access-control.md)
[(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | +| [SRT Access Control
(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | | [SRT Connection Bonding](features/bonding-intro.md) | [features](features/) | [bonding-intro.md](features/bonding-intro.md) | Introduction to Connection Bonding. Description
of group (bonded) connections. | -| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | -| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | -| [Live Streaming](features/live-streaming.md)
[Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | -| [SRT Packet](features/packet-filtering-and-fec.md)
[Filtering & FEC](features/packet-filtering-and-fec.md) | [features](features/) | [packet-filtering-and-fec.md](features/packet-filtering-and-fec.md) | Description of SRT packet filtering mechanism,
including FEC. | | [SRT Socket Groups](features/socket-groups.md) | [features](features/) | [socket-groups.md](features/socket-groups.md) | Description of socket groups in SRT (Connection
Bonding). Here you will also find the information
regarding the `srt-test-live` application for testing
Connection Bonding. | +| [SRT Connection Bonding: Main/Backup][main-backup] | [features](features/) | [bonding-main-backup.md][main-backup] | SRT Main/Backup Connection Bonding. | +| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption][srt-rfc-sec-6] of the [SRT RFC][srt-rfc] additionally. | +| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake][srt-rfc-sec-3-2-1] and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC][srt-rfc] additionally. | +| [Live Streaming
Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC][srt-rfc]. | +| [SRT Packet
Filtering & FEC][packet-filter] | [features](features/) | [packet-filtering-and-fec.md][packet-filter] | Description of SRT packet filtering mechanism,
including FEC. | | | | | | ## Sample Applications | Document Title | Folder | File Name | Description | | :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | -| [Using the](apps/srt-live-transmit.md)
[`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | -| [Using the](apps/srt-multiplex.md)
[`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | -| [Using the](apps/srt-tunnel.md)
[`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the
`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | +| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | | | | | | ## Miscellaneous | Document Title | Folder | File Name | Description | | :------------------------------------------------- | :---------------------------- | :---------------------------------------------------- | :----------------------------------------------------------- | -| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction][srt-rfc-sec-1] of the [SRT RFC][srt-rfc]. | | | | | | + +[srt-rfc]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00 +[srt-rfc-sec-1]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1 +[srt-rfc-sec-3-2-1]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1 +[srt-rfc-sec-6]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6 + +[main-backup]: features/bonding-main-backup.md +[packet-filter]: features/packet-filtering-and-fec.md diff --git a/docs/features/bonding-main-backup.md b/docs/features/bonding-main-backup.md new file mode 100644 index 000000000..59fe78834 --- /dev/null +++ b/docs/features/bonding-main-backup.md @@ -0,0 +1,248 @@ +# SRT Connection Bonding: Main/Backup + +I. [Introduction](#i-introduction) +II. [Mode Overview](#ii-mode-overview) +III. [Sender Logic](#iii-sender-logic) +IV. [Sending Algorithm](#iV-sending-algorithm) + +## I. Introduction + +*SRT Main/Backup Switching* saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. + +This feature is useful for broadcasting where the traffic costs for a main link are relatively low, and the main link is reasonably reliable. To make sure a transmission doesnā€™t fail, one or several backup connections are on stand-by. These backup connections ensure reliability, but the traffic costs may be high. To save costs, the backup links are only activated when the main link fails or doesnā€™t achieve the required throughput. + +The goal of SRT's main/backup bonding mode is to identify a potential link break before it is finally confirmed, thus providing a time window to seamlessly switch to one of the backup links. + +## II. Mode Overview + +### Mode Constraints + +**Main constraints** of the main/backup mode are: + +- **only one active link at a time**, except for the period of switching to one of the backup links or to a higher weight link; +- **seamless switching** to one of the backup links ideally without packet drops, but a certain period of packet drop is acceptable during the switch if reasonable (e.g. severe conditions). + +Transmission happens over the main link until it is considered broken or is presumably about to become broken. + +### Sensitivity Modes + +The only sensitivity mode implemented at the moment is a **precautions switch** from an unstable main path to a backup path before an unstable path becomes broken. The goal is to predict an upcoming link breakage before it finally happens, and be ready to switch to an activated backup link loosing as minimum packet as possible. + +Potential improvement for the future may be an additional sensitivity mode: **handover switch**. For the cases where low latency and packet loss is not that critical, the switch can take place once the link is actually broken, without trying to predict it. As the main path is already broken, activating the backup path happens late and there will be a discontinuity in streaming (data loss during the switch). + +### Mode Limitations + +Detecting possible link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY`, there are good chances to not lose a single packet. Therefore, one of the limitations (or usage guidelines) is `SRTO_PEERLATENCY ā‰„ 3ƗRTT`. + +The logic of the main/backup sending algorithm is triggered each time an application submits new payload (calls `srt_sendmsg2(..)`). This imposes two limitations: + +- new packets are better be submitted no later than every 50 or 60 ms to trigger the logic with enough frequency (bitrate should be > 180 kbps); +- file transmission logic does not fit well, and not supported anyway. + +## III. Sender Logic + +### Member Link State + +In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. + +Member link status can be: + +1. **SRT_GST_PENDING**. The link is visible, but the connection hasn't been established yet. The link cannot be used for data transmission yet. +2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per 1 second. +3. **SRT_GST_RUNNING** (**active**). The link is being used for payload transmission. + 1. **Fresh-Activated**. A link was freshly (newly) activated, and it is too early to qualify it as either stable or unstable. + 2. **Stable**. Active link is considered stable. + 3. **Unstable**. Active link is considered unstable, e.g. response time exceeded some threshold. + 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold), now a response, which makes it potentially stable again. +4. **SRT_GST_BROKEN**. The link has just been detected broken. It is about to be closed and removed from the group. + +### Member State Transition + +The initial state for group sender logic to operate on a member is once a member socket is connected and transitions to **SRT_GST_IDLE**. + +| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (unstable) | q4 (wary) | q5 =>(broken) | +| ---------------------------------------------------------------- | ---------------------------------- | -------------------------------- | -------------------------------- | ------------------------------ | ---------- | ------------- | +| Last response < LST* | x | q1 | q2 | **q4** (`m_tsWarySince` = now) | x | x | +| Last response >= LST* | x | **q3** (`tsUnstableSince` = now) | **q3** (`tsUnstableSince` = now) | x | **q3** | x | +| `tsUnstableSince`!= 0 and (now - `tsUnstableSince`> PEERIDLETMO) | x | x | x | **q5** | **q5** | x | +| Probing period is over | x | q2 / `tsFreshActivation` = 0 | x | x | **q2** | x | +| Activate (group decision) | **q1** (`tsFreshActivation` = now) | x | x | x | x | x | +| Silence (group decision) | x | :question: | **q0** | x | :question: | x | +| Close/break (external event) | **q5** | **q5** | **q5** | **q5** | **q5** | x | + +Member activation happens according to conditions described in the [Member Activation](#member-activation) section. In case of activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. + +A member in the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state can either become **q2** (**SRT_GST_RUNNING**: **Stable**) or **q3** (**SRT_GST_RUNNING**: **Unstable**). + +Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. A member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. + +An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) can **either**: + +- transition back to the **q2** (**SRT_GST_RUNNING**: **Stable**) state through the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state; +- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); +- transition to the **q5** (**SRT_GST_BROKEN**) state (be closed and removed from the group). + +A member in the **q5** (**SRT_GST_BROKEN**) state will eventually be closed and removed from the group. + +### Member Ordering by Priority + +Comparing two members, the one is ordered before, for which one of the following ordering conditions wins (in the order of priority). + +1. Higher weight (highest priority) +2. By backup state (if equal weight): + 1. **SRT_GST_RUNNING**: **Stable** + 2. **SRT_GST_RUNNING**: **Fresh-Activated** + 3. **SRT_GST_RUNNING**: **Unstable-Wary** + 4. **SRT_GST_RUNNING**: **Unstable** + 5. **SRT_GST_BROKEN** + 6. **SRT_GST_IDLE** (**stand by**) + 7. **SRT_GST_PENDING** +3. By the Socket ID (lower value first). + +For example, an unstable member with a higher weight is ordered before a stable member with lower weight. + +**Potential Improvement**: Order by connection start time (older connection comes first) before comparing socket IDs. + +### Member Activation + +Member activation means transitioning an idle (stand by) member with status **SRT_GST_IDLE** into a **SRT_GST_RUNNING**: **Fresh-Activated** state. Member activation time is saved as `tsFreshActivation = CurrentTime`. + +Activation is needed if one of the following is true: + +1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. +2. The weight of one of idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. + +An idle link to be activated is taken from the top of the list of idle links, sorted according to the [Member Ordering by Priority](#send-member-ordering). + +A member remains in the **SRT_GST_RUNNING**: **Fresh-Activated** state while `CurrentTime - tsFreshActivation > ProbingPeriod`, i.e. for the whole probing period: + + `ProbingPeriod = ILST + 50ms` + +Here the **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, + +- `LSTmin = 60ms` ; +- `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket. + +### Qualifying a Member as Unstable + +A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) by any of the reasons described below. + +#### Unstable by Response Timeout + +A member link is considered unstable if the time elapsed since the last response from the peer (`LastRspTime`) exceeds link stability timeout: + +`CurrentTime - LastRspTime > LST` + +where + +- `CurrentTime` is the current time when the next data packet **is submitted to a group** for sending; +- `LastRspTime` is the time of receiving the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) from the SRT receiver by the SRT sender; for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ā‰„ tsFreshActivation` ; +- `LST` (Link Stability Timeout) is a dynamic value of stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. + +Link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). + +For a member in **SRT_GST_RUNNING**: **Fresh-Activated** state `LST = ILST` (see [Member Activation](#member-activation)). + +For active (**SRT_GST_RUNNING**) members in states **different** from **SRT_GST_RUNNING**: **Fresh-Activated** `LST` is calculated as follows: + +`LST = 2 * SRTT + 4 * RTTVar,` and `LSTmin ā‰¤ LST ā‰¤ SRTO_PEERLATENCY`, + +where + +- `LSTmin = 60ms` ; +- `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket; +- `SRTT` and `RTTVar` are smoothed RTT and RTT variance calculated on an individual socket member in runtime as described is SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. + +#### Unstable by Sending Failure + +If sending a packet (`srt_sendmsg2(..)`) over a member SRT socket has failed with error `SRT_EASYNCSND`, the member is qualified as **q3** (**SRT_GST_RUNNING**: **Unstable**). + +This error indicates that there was not enough free space in sender's buffer to take this data packet for sending. It should not happen under normal conditions and if buffers were configured correctly this kind of error indicates some possible congestion on a path. + +#### Unstable by Sender-Side Packet Drops + +If a member has dropped (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) a packet **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. + +#### Unstable by Receiver-Side Packet Drops (TBD) + +**IMPORTANT: For the time being, the main backup algorithm does not react on lost packets or packets dropped by the receiver.** Note that SRT sender does not know the drop rate on the receiver's side. Receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packet with a total number of packets dropped by the receiver. + +### Qualifying a Member as Broken + +#### Broken by Peer Idle Timeout + +Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket is to be closed by the SRT library. It is also removed from a group. + +#### Broken by Remaining Unstable for Too Long + +The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: + +`CurrentTime - tsUnstableSince > SRTO_PEERIDLETIMEO`. + +### Qualifying a Member as Stable + +Only a member SRT socket in the active (**SRT_GST_RUNNING**) state can be qualified as stable. + +#### Fresh-Activated Becomes Stable + +A fresh activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualifies as unstable). The probing period is defined in the [Member Activation](#member-activation) section. + +#### Unstable Becomes Stable + +If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to a stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. + +If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state for `4 Ɨ SRTO_PEERLATENCY`, it can transition to the **SRT_GST_RUNNING**: **Stable** state. + +*Note that if a member becomes unstable **q3** (**SRT_GST_RUNNING**: **Unstable**) again, the `tsWarySince` time will be reset on the next transition to the **Unstable-Wary** state.* + +### Silencing an Active Member + +There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. + +In order to select a stable member to remain active the [Member Ordering by Priority](#send-member-ordering) is applied. All active members ordered after the first stable member are silenced. All active members ordered before the first stable member in the list, including the stable member, remain active. + +## IV. Sending Algorithm + +Group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. + +### 1. Qualify Member States + +Before sending a packet, all member links are qualified to the states described above. + +### 2. Sending the Same Packet over Active Links + +The same data packet is sent (duplicated) over all members qualified as active (**SRT_GST_RUNNING**). + +**If sending fails, the link is re-qualified as unstable** as described in the [Unstable by Sending Failure](#unstable-by-sending-failure) section. + +### 3. Save the Packet Being Sent to the Group SND Buffer + +The group has a separate buffer for packets being sent and to yet acknowledged. If a member socket gets broken, those packets can be resent over a fresh-activated backup member. + +### 4. Check if Backup Link Activation is Needed + +See the [Member Activation](#member-activation) section. + +### 5. [IF] Activate Idle Link + +If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket.. If sending fails (see [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. + +### 6. Check Pending and Broken Sockets + +Check if there are pending sockets that failed to connect, and should be removed from the group. + +Check if there are broken sockets to be removed from the group. + +Check if there are member socket unstable for to long and to be requested to become broken. + +### 7. Wait for Sending Success + +There may be a situation when none of sending succeeded, but there are active members. + +In case of a non-blocking operation mode of the group, the group returns `SRT_EASYNCSND` error. + +In case of a blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned then), or at least one member successfully takes a data packet for sending. + +### 8. Silence Redundant Members + +The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied on this step. From de68ae9d31405799ad7c44c01775e2d4c9c981b3 Mon Sep 17 00:00:00 2001 From: stevomatthews Date: Tue, 11 May 2021 10:45:01 +0200 Subject: [PATCH 169/790] [docs] Corrections to the main backup doc --- docs/features/bonding-main-backup.md | 122 +++++++++++++-------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/features/bonding-main-backup.md b/docs/features/bonding-main-backup.md index 59fe78834..76a6e27d6 100644 --- a/docs/features/bonding-main-backup.md +++ b/docs/features/bonding-main-backup.md @@ -7,60 +7,60 @@ IV. [Sending Algorithm](#iV-sending-algorithm) ## I. Introduction -*SRT Main/Backup Switching* saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. +*SRT Main/Backup Switching* mode saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. This feature is useful for broadcasting where the traffic costs for a main link are relatively low, and the main link is reasonably reliable. To make sure a transmission doesnā€™t fail, one or several backup connections are on stand-by. These backup connections ensure reliability, but the traffic costs may be high. To save costs, the backup links are only activated when the main link fails or doesnā€™t achieve the required throughput. -The goal of SRT's main/backup bonding mode is to identify a potential link break before it is finally confirmed, thus providing a time window to seamlessly switch to one of the backup links. +The goal of SRT's main/backup bonding mode is to identify a potential link break before it happens, thus providing a time window within which to seamlessly switch to one of the backup links. ## II. Mode Overview ### Mode Constraints -**Main constraints** of the main/backup mode are: +The **main constraints** of the main/backup switching mode are: -- **only one active link at a time**, except for the period of switching to one of the backup links or to a higher weight link; -- **seamless switching** to one of the backup links ideally without packet drops, but a certain period of packet drop is acceptable during the switch if reasonable (e.g. severe conditions). +- **only one active link at a time**, except when switching to one of the backup links or to a link with a higher weight; +- **seamless switching** to one of the backup links ideally happens without packet drops, but dropping packets for a certain period of time is acceptable during the switch under certain circumstances (e.g. severe conditions). Transmission happens over the main link until it is considered broken or is presumably about to become broken. -### Sensitivity Modes +### Sensitivity Levels -The only sensitivity mode implemented at the moment is a **precautions switch** from an unstable main path to a backup path before an unstable path becomes broken. The goal is to predict an upcoming link breakage before it finally happens, and be ready to switch to an activated backup link loosing as minimum packet as possible. +The only sensitivity level implemented at the moment is a **pre-emptive switch** to a backup path from an unstable main path before it breaks. The goal is to predict an upcoming link breakage before it happens, and to be ready to switch to an activated backup link while losing as few packets as possible. -Potential improvement for the future may be an additional sensitivity mode: **handover switch**. For the cases where low latency and packet loss is not that critical, the switch can take place once the link is actually broken, without trying to predict it. As the main path is already broken, activating the backup path happens late and there will be a discontinuity in streaming (data loss during the switch). +An additional sensitivity level (**handover switch**) may be added in the future for cases where low latency and packet loss is not critical. The switch would take place once the link is actually broken, without trying to predict it thereby reducing processing overhead. Since the main path in this case is already broken, there would be a delay associated with activating the backup path resulting in a discontinuity in streaming (data loss during the switch). ### Mode Limitations -Detecting possible link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY`, there are good chances to not lose a single packet. Therefore, one of the limitations (or usage guidelines) is `SRTO_PEERLATENCY ā‰„ 3ƗRTT`. +Detecting a potential link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY` interval, there is a good chance that not a single packet will be lost. Therefore, one of the limitations (or usage guidelines) is to set `SRTO_PEERLATENCY ā‰„ 3ƗRTT`. The logic of the main/backup sending algorithm is triggered each time an application submits new payload (calls `srt_sendmsg2(..)`). This imposes two limitations: -- new packets are better be submitted no later than every 50 or 60 ms to trigger the logic with enough frequency (bitrate should be > 180 kbps); -- file transmission logic does not fit well, and not supported anyway. +- it is better to submit new packets no later than every 50 or 60 ms to trigger the logic with enough frequency (the bitrate should be > 180 kbps); +- file transmission logic does not fit well with this algorithm, and is not supported. ## III. Sender Logic ### Member Link State -In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. +In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from the SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. Member link status can be: 1. **SRT_GST_PENDING**. The link is visible, but the connection hasn't been established yet. The link cannot be used for data transmission yet. -2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per 1 second. +2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per second. 3. **SRT_GST_RUNNING** (**active**). The link is being used for payload transmission. 1. **Fresh-Activated**. A link was freshly (newly) activated, and it is too early to qualify it as either stable or unstable. 2. **Stable**. Active link is considered stable. - 3. **Unstable**. Active link is considered unstable, e.g. response time exceeded some threshold. - 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold), now a response, which makes it potentially stable again. -4. **SRT_GST_BROKEN**. The link has just been detected broken. It is about to be closed and removed from the group. + 3. **Unstable**. Active link is considered unstable, e.g. response time has exceeded some threshold. + 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold) until a new response makes it potentially stable again. +4. **SRT_GST_BROKEN**. The link has just been detected to be broken. It is about to be closed and removed from the group. ### Member State Transition -The initial state for group sender logic to operate on a member is once a member socket is connected and transitions to **SRT_GST_IDLE**. +The initial state for group sender logic to operate on a member is once the member socket is connected and transitions to **SRT_GST_IDLE**. -| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (unstable) | q4 (wary) | q5 =>(broken) | +| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (Unstable) | q4 (Wary) | q5 =>(Broken) | | ---------------------------------------------------------------- | ---------------------------------- | -------------------------------- | -------------------------------- | ------------------------------ | ---------- | ------------- | | Last response < LST* | x | q1 | q2 | **q4** (`m_tsWarySince` = now) | x | x | | Last response >= LST* | x | **q3** (`tsUnstableSince` = now) | **q3** (`tsUnstableSince` = now) | x | **q3** | x | @@ -70,23 +70,23 @@ The initial state for group sender logic to operate on a member is once a member | Silence (group decision) | x | :question: | **q0** | x | :question: | x | | Close/break (external event) | **q5** | **q5** | **q5** | **q5** | **q5** | x | -Member activation happens according to conditions described in the [Member Activation](#member-activation) section. In case of activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. +Member activation happens according to conditions described in the [Member Activation](#member-activation) section. Upon activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. A member in the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state can either become **q2** (**SRT_GST_RUNNING**: **Stable**) or **q3** (**SRT_GST_RUNNING**: **Unstable**). -Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. A member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. +Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. If the conditions are met, the member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. -An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) can **either**: +An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) **either**: - transition back to the **q2** (**SRT_GST_RUNNING**: **Stable**) state through the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state; -- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); +- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); or - transition to the **q5** (**SRT_GST_BROKEN**) state (be closed and removed from the group). A member in the **q5** (**SRT_GST_BROKEN**) state will eventually be closed and removed from the group. ### Member Ordering by Priority -Comparing two members, the one is ordered before, for which one of the following ordering conditions wins (in the order of priority). +When comparing two members, one is ordered before the other depending on which of the following ordering conditions applies (in the order of priority). 1. Higher weight (highest priority) 2. By backup state (if equal weight): @@ -105,41 +105,41 @@ For example, an unstable member with a higher weight is ordered before a stable ### Member Activation -Member activation means transitioning an idle (stand by) member with status **SRT_GST_IDLE** into a **SRT_GST_RUNNING**: **Fresh-Activated** state. Member activation time is saved as `tsFreshActivation = CurrentTime`. +*Member activation* means transitioning an idle (stand by) member with status **SRT_GST_IDLE** to a **SRT_GST_RUNNING**: **Fresh-Activated** state. The time it takes to activate a member is saved as `tsFreshActivation = CurrentTime`. Activation is needed if one of the following is true: -1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. -2. The weight of one of idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. +1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. +2. The weight of one of the idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. -An idle link to be activated is taken from the top of the list of idle links, sorted according to the [Member Ordering by Priority](#send-member-ordering). +An idle link to be activated is taken from the top of the list of idle links, sorted according to [member ordering priority](#send-member-ordering). A member remains in the **SRT_GST_RUNNING**: **Fresh-Activated** state while `CurrentTime - tsFreshActivation > ProbingPeriod`, i.e. for the whole probing period: `ProbingPeriod = ILST + 50ms` -Here the **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, +Here **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, - `LSTmin = 60ms` ; - `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket. ### Qualifying a Member as Unstable -A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) by any of the reasons described below. +A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) for any of the reasons described below. -#### Unstable by Response Timeout +#### Unstable due to response timeout -A member link is considered unstable if the time elapsed since the last response from the peer (`LastRspTime`) exceeds link stability timeout: +A member link is considered unstable if the time elapsed since the last response from a peer (`LastRspTime`) exceeds the link stability timeout: `CurrentTime - LastRspTime > LST` where -- `CurrentTime` is the current time when the next data packet **is submitted to a group** for sending; -- `LastRspTime` is the time of receiving the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) from the SRT receiver by the SRT sender; for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ā‰„ tsFreshActivation` ; -- `LST` (Link Stability Timeout) is a dynamic value of stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. +- `CurrentTime` is the time when the next data packet **is submitted to a group** for sending; +- `LastRspTime` is the time when the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) was received from the SRT receiver by the SRT sender for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ā‰„ tsFreshActivation`; +- `LST` (Link Stability Timeout) is a dynamic value for stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. -Link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). +The link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). For a member in **SRT_GST_RUNNING**: **Fresh-Activated** state `LST = ILST` (see [Member Activation](#member-activation)). @@ -149,33 +149,33 @@ For active (**SRT_GST_RUNNING**) members in states **different** from **SRT_GST where -- `LSTmin = 60ms` ; +- `LSTmin = 60ms`; - `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket; -- `SRTT` and `RTTVar` are smoothed RTT and RTT variance calculated on an individual socket member in runtime as described is SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. +- `SRTT` and `RTTVar` are smoothed RTT and RTT variances calculated on an individual socket member in runtime as described in the SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. -#### Unstable by Sending Failure +#### Unstable due to sending failure If sending a packet (`srt_sendmsg2(..)`) over a member SRT socket has failed with error `SRT_EASYNCSND`, the member is qualified as **q3** (**SRT_GST_RUNNING**: **Unstable**). -This error indicates that there was not enough free space in sender's buffer to take this data packet for sending. It should not happen under normal conditions and if buffers were configured correctly this kind of error indicates some possible congestion on a path. +This error indicates that there was not enough free space in the sender's buffer to accept this data packet for sending. It should not happen under normal conditions and if buffers are configured correctly this kind of error indicates some possible congestion on a path. -#### Unstable by Sender-Side Packet Drops +#### Unstable due to sender-side packet drops -If a member has dropped (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) a packet **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. +If a member has dropped a packet (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. -#### Unstable by Receiver-Side Packet Drops (TBD) +#### Unstable due to receiver-side packet drops (TBD) -**IMPORTANT: For the time being, the main backup algorithm does not react on lost packets or packets dropped by the receiver.** Note that SRT sender does not know the drop rate on the receiver's side. Receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packet with a total number of packets dropped by the receiver. +**IMPORTANT: For the time being, the main backup algorithm does not react to lost packets or packets dropped by the receiver.** Note that an SRT sender does not know the drop rate on the receiver's side. A receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packets to include the total number of packets dropped by the receiver. ### Qualifying a Member as Broken -#### Broken by Peer Idle Timeout +#### Broken due to peer idle timeout -Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket is to be closed by the SRT library. It is also removed from a group. +Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by the [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket will be closed by the SRT library. It is also removed from a group. -#### Broken by Remaining Unstable for Too Long +#### Broken due to remaining unstable for too long -The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: +The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by the [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: `CurrentTime - tsUnstableSince > SRTO_PEERIDLETIMEO`. @@ -183,13 +183,13 @@ The only additional condition to break an SRT connection from a group is if a me Only a member SRT socket in the active (**SRT_GST_RUNNING**) state can be qualified as stable. -#### Fresh-Activated Becomes Stable +#### Freshly-activated becomes stable -A fresh activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualifies as unstable). The probing period is defined in the [Member Activation](#member-activation) section. +A freshly activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualified as unstable). The probing period is defined in the [Member Activation](#member-activation) section. -#### Unstable Becomes Stable +#### Unstable becomes stable -If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to a stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. +If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to the stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state for `4 Ɨ SRTO_PEERLATENCY`, it can transition to the **SRT_GST_RUNNING**: **Stable** state. @@ -197,17 +197,17 @@ If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state ### Silencing an Active Member -There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. +There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. In order to select a stable member to remain active the [Member Ordering by Priority](#send-member-ordering) is applied. All active members ordered after the first stable member are silenced. All active members ordered before the first stable member in the list, including the stable member, remain active. ## IV. Sending Algorithm -Group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. +The group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. ### 1. Qualify Member States -Before sending a packet, all member links are qualified to the states described above. +Before sending a packet, all member links are qualified according to the states described above. ### 2. Sending the Same Packet over Active Links @@ -217,7 +217,7 @@ The same data packet is sent (duplicated) over all members qualified as active ( ### 3. Save the Packet Being Sent to the Group SND Buffer -The group has a separate buffer for packets being sent and to yet acknowledged. If a member socket gets broken, those packets can be resent over a fresh-activated backup member. +The group has a separate buffer for packets being sent and to be acknowledged. If a member socket gets broken, those packets can be resent over a freshly-activated backup member. ### 4. Check if Backup Link Activation is Needed @@ -225,7 +225,7 @@ See the [Member Activation](#member-activation) section. ### 5. [IF] Activate Idle Link -If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket.. If sending fails (see [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. +If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket. If sending fails (see the [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. ### 6. Check Pending and Broken Sockets @@ -233,16 +233,16 @@ Check if there are pending sockets that failed to connect, and should be removed Check if there are broken sockets to be removed from the group. -Check if there are member socket unstable for to long and to be requested to become broken. +Check if there are member socket unstable for too long that should be requested to transition to the `broken` state. ### 7. Wait for Sending Success -There may be a situation when none of sending succeeded, but there are active members. +There may be a situation where no sending has succeeded, but there are active members. -In case of a non-blocking operation mode of the group, the group returns `SRT_EASYNCSND` error. +If the group is in non-blocking operation mode, the group returns `SRT_EASYNCSND` error. -In case of a blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned then), or at least one member successfully takes a data packet for sending. +If the group is in blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned), or at least one member successfully accepts a data packet for sending. ### 8. Silence Redundant Members -The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied on this step. +The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied in this step. From 34e14abe3a88863e31ac160b6d321722d89bfd33 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 11 May 2021 18:45:57 +0800 Subject: [PATCH 170/790] [core] Fix use-after-free in rendezvous queue (#1919) A socket changing its m_bConnecting state from true to false will no longer be removed from the queue when closing. See CUDT::closeInternal(). Therefore it must be removed from the queue in CRendezvousQueue::updateConnStatus(..). --- srtcore/queue.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 61400df22..8fe5809bc 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1009,11 +1009,6 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; ufailed.push_back(fi); - /* - * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. - * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) - * and may crash here on next pass. - */ // i_next was preincremented, but this is guaranteed to point to // the element next to erased one. i_next = m_lRendezvousID.erase(i); @@ -1100,7 +1095,13 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con for (vector::iterator i = ufailed.begin(); i != ufailed.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); + /* + * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. + * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) + * and may crash on next pass. + */ i->u->m_bConnecting = false; + remove(i->u->m_SocketID); // DO NOT close the socket here because in this case it might be // unable to get status from at the right moment. Also only member From 9a09b5374e018468ab1673980fcfce31bcf2ebd3 Mon Sep 17 00:00:00 2001 From: Lewis Kirkaldie Date: Wed, 12 May 2021 09:27:03 +0100 Subject: [PATCH 171/790] [build] Windows Build Script improvements (#1994). Added custom build directory and VCPKG OpenSSL. --- .gitignore | 3 ++ CMakeLists.txt | 7 ++- scripts/build-windows.ps1 | 80 +++++++++++++++++++++++++++----- scripts/set-version-metadata.ps1 | 9 ++++ srtcore/version.h.in | 2 +- 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index e179ad146..10f985468 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ _*/ # Ignode Visual Studio Code temp folder .vs/ .vscode/ + +# Ignore vcpkg submodule +vcpkg/ diff --git a/CMakeLists.txt b/CMakeLists.txt index df43a1e26..98d360671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -790,9 +790,14 @@ MafReadDir(srtcore filelist.maf # Auto generated version file and add it to the HEADERS_srt list. if(DEFINED ENV{APPVEYOR_BUILD_NUMBER}) set(SRT_VERSION_BUILD ON) - set(APPVEYOR_BUILD_NUMBER_STRING $ENV{APPVEYOR_BUILD_NUMBER}) + set(CI_BUILD_NUMBER_STRING $ENV{APPVEYOR_BUILD_NUMBER}) message(STATUS "AppVeyor build environment detected: Adding build number to version header") endif() +if(DEFINED ENV{TEAMCITY_VERSION}) + set(SRT_VERSION_BUILD ON) + set(CI_BUILD_NUMBER_STRING $ENV{CI_BUILD_COUNTER}) + message(STATUS "TeamCity build environment detected: Adding build counter to version header") +endif() configure_file("srtcore/version.h.in" "version.h" @ONLY) diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index 47ccb97f5..ab2d19d2d 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -6,7 +6,7 @@ # # By default produces a VS2019 64-bit Release binary using C++11 threads, without # encryption or unit tests enabled, but including test apps. -# Before enabling any encryption options, please install OpenSSL (or customize) +# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build ################################################################################ param ( @@ -17,7 +17,9 @@ param ( [Parameter()][String]$STATIC_LINK_SSL = "OFF", [Parameter()][String]$CXX11 = "ON", [Parameter()][String]$BUILD_APPS = "ON", - [Parameter()][String]$UNIT_TESTS = "OFF" + [Parameter()][String]$UNIT_TESTS = "OFF", + [Parameter()][String]$BUILD_DIR = "_build", + [Parameter()][String]$VCPKG_OPENSSL = "OFF" ) # cmake can be optionally installed (useful when running interactively on a developer station). @@ -65,7 +67,7 @@ if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERAT if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; } # clear any previous build and create & enter the build directory -$buildDir = Join-Path "$projectRoot" "_build" +$buildDir = Join-Path "$projectRoot" "$BUILD_DIR" Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs" Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null @@ -88,19 +90,20 @@ if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { Start-Process $cmakeMsiFile -Wait Remove-Item $cmakeMsiFile Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script" - exit + throw } else{ Write-Output "Quitting because cmake is required" - exit + throw } } +# get pthreads from nuget if CXX11 is not enabled if ( $CXX11 -eq "OFF" ) { # get pthreads (this is legacy, and is only availble in nuget for VS2015 and VS2013) if ( $VS_VERSION -gt 2015 ) { Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build" - exit + throw } if ( $DEVENV_PLATFORM -eq 'Win32' ) { nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages @@ -110,6 +113,7 @@ if ( $CXX11 -eq "OFF" ) { } } +# check to see if static SSL linking was requested, and enable encryption if not already ON if ( $STATIC_LINK_SSL -eq "ON" ) { if ( $ENABLE_ENCRYPTION -eq "OFF" ) { # requesting a static link implicitly requires encryption support @@ -118,14 +122,63 @@ if ( $STATIC_LINK_SSL -eq "ON" ) { } } +# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON +if ( $VCPKG_OPENSSL -eq "ON" ) { + if ( $ENABLE_ENCRYPTION -eq "OFF" ) { + # requesting VCPKG to provide OpenSSL requires encryption support + Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON" + $ENABLE_ENCRYPTION = "ON" + } +} + # build the cmake command flags from arguments $cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + "-DENABLE_STDCXX_SYNC=$CXX11 " + "-DENABLE_APPS=$BUILD_APPS " + "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + - "-DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " + "-DENABLE_UNITTESTS=$UNIT_TESTS" +# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package +if ( $VCPKG_OPENSSL -eq 'ON' ) { + Push-Location $projectRoot + Write-Output "Cloning VCPKG into: $(Get-Location)" + if (Test-Path -Path ".\vcpkg") { + Set-Location .\vcpkg + git pull + } else { + git clone https://github.com/microsoft/vcpkg + Set-Location .\vcpkg + } + + .\bootstrap-vcpkg.bat + + if($DEVENV_PLATFORM -EQ "x64"){ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x64-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static" + } + else{ + .\vcpkg install openssl:x64-windows + } + } + else{ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x86-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static" + } + else{ + .\vcpkg install openssl:x86-windows + } + } + + .\vcpkg integrate install + Pop-Location + $cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake" +} +else { + $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " +} + # cmake uses a flag for architecture from vs2019, so add that as a suffix if ( $VS_VERSION -eq '2019' ) { $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" @@ -143,7 +196,8 @@ Invoke-Expression "& $execVar" # check build ran OK, exit if cmake failed if( $LASTEXITCODE -ne 0 ) { - return $LASTEXITCODE + Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" + throw } $ErrorActionPreference = "Stop" @@ -161,13 +215,17 @@ if ( $null -eq $msBuildPath ) { $vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue if ( $null -eq $vsWherePath ) { Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again" - exit + throw } } - $msBuildPath = & $vsWherePath -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 + $msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 + if ( $null -eq $msBuildPath ) { + Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation" + throw + } } -& $msBuildPath SRT.sln /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM +& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM # return to the directory previously occupied before running the script Pop-Location diff --git a/scripts/set-version-metadata.ps1 b/scripts/set-version-metadata.ps1 index cd36cc37d..050838e5e 100644 --- a/scripts/set-version-metadata.ps1 +++ b/scripts/set-version-metadata.ps1 @@ -27,6 +27,15 @@ if($Env:APPVEYOR){ Update-AppveyorBuild -Version "$majorVer.$minorVer.$patchVer.$buildNum" $FileDescriptionBranchCommitValue = "$Env:APPVEYOR_REPO_NAME - $($Env:APPVEYOR_REPO_BRANCH) ($($Env:APPVEYOR_REPO_COMMIT.substring(0,8)))" } +if($Env:TEAMCITY_VERSION){ + #make TeamCity update with this new version number + Write-Output "##teamcity[buildNumber '$majorVer.$minorVer.$patchVer.$buildNum']" + Write-Output "##teamcity[setParameter name='MajorVersion' value='$majorVer']" + Write-Output "##teamcity[setParameter name='MinorVersion' value='$minorVer']" + Write-Output "##teamcity[setParameter name='PatchVersion' value='$patchVer']" + Write-Output "##teamcity[setParameter name='BuildVersion' value='$buildNum']" + $FileDescriptionBranchCommitValue = "$majorVer.$minorVer.$patchVer.$buildNum - ($($Env:BUILD_VCS_NUMBER.substring(0,8)))" +} #find C++ resource files and update file description with branch / commit details $FileDescriptionStringRegex = '(\bVALUE\s+\"FileDescription\"\s*\,\s*\")([^\"]*\\\")*[^\"]*(\")' diff --git a/srtcore/version.h.in b/srtcore/version.h.in index 72ce89bcb..c32ac1226 100644 --- a/srtcore/version.h.in +++ b/srtcore/version.h.in @@ -24,7 +24,7 @@ written by #define SRT_VERSION_MAJOR @SRT_VERSION_MAJOR@ #define SRT_VERSION_MINOR @SRT_VERSION_MINOR@ #define SRT_VERSION_PATCH @SRT_VERSION_PATCH@ -#cmakedefine SRT_VERSION_BUILD @APPVEYOR_BUILD_NUMBER_STRING@ +#cmakedefine SRT_VERSION_BUILD @CI_BUILD_NUMBER_STRING@ #define SRT_VERSION_STRING "@SRT_VERSION@" #define SRT_VERSION_VALUE \ From 930d3468ae256dcb959d96d3a2a38c06c10a4645 Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Wed, 12 May 2021 11:50:20 +0200 Subject: [PATCH 172/790] [build] Added script to build the Windows installer for SRT libraries (#1995). See the scripts/win-installer/README.md file for details. --- scripts/win-installer/.gitignore | 10 + scripts/win-installer/README.md | 33 +++ scripts/win-installer/build-win-intaller.ps1 | 212 ++++++++++++++++++ scripts/win-installer/install-nsis.ps1 | 122 +++++++++++ scripts/win-installer/install-openssl.ps1 | 119 ++++++++++ scripts/win-installer/libsrt.nsi | 217 +++++++++++++++++++ scripts/win-installer/libsrt.props | 19 ++ 7 files changed, 732 insertions(+) create mode 100644 scripts/win-installer/.gitignore create mode 100644 scripts/win-installer/README.md create mode 100644 scripts/win-installer/build-win-intaller.ps1 create mode 100644 scripts/win-installer/install-nsis.ps1 create mode 100644 scripts/win-installer/install-openssl.ps1 create mode 100644 scripts/win-installer/libsrt.nsi create mode 100644 scripts/win-installer/libsrt.props diff --git a/scripts/win-installer/.gitignore b/scripts/win-installer/.gitignore new file mode 100644 index 000000000..ed44eef33 --- /dev/null +++ b/scripts/win-installer/.gitignore @@ -0,0 +1,10 @@ +tmp +installers +*.exe +*~ +~* +.#* +*.bak +*.autosave +.DS_Store +._* diff --git a/scripts/win-installer/README.md b/scripts/win-installer/README.md new file mode 100644 index 000000000..205e03893 --- /dev/null +++ b/scripts/win-installer/README.md @@ -0,0 +1,33 @@ +## SRT Static Libraries Installer for Windows + +This directory contains scripts to build a binary installer for +libsrt on Windows systems for Visual Studio applications using SRT. + +### Building Windows applications with libsrt + +After installing the libsrt binary, an environment variable named `LIBSRT` is +defined to the installation root (typically `C:\Program Files (x86)\libsrt`). + +In this directory, there is a Visual Studio property file named `libsrt.props`. +Simply reference this property file in your Visual Studio project to use libsrt. + +You can also do that manually by editing the application project file (the XML +file named with a `.vcxproj` extension). Add the following line just before +the end of the file: + +~~~ + +~~~ + +### Building the installer + +The first two steps need to be executed once only. Only the last step needs +to be repeated each time a new version of libsrt is available. + +- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. + This can be done automatically by running the PowerShell script `install-openssl.ps1`. +- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. + This can be done automatically by running the PowerShell script `install-nsis.ps1`. +- Build the libsrt installer by running the PowerShell script `build-win-installer.ps1`. + +The installer is then available in the directory `installers`. diff --git a/scripts/win-installer/build-win-intaller.ps1 b/scripts/win-installer/build-win-intaller.ps1 new file mode 100644 index 000000000..261c457fa --- /dev/null +++ b/scripts/win-installer/build-win-intaller.ps1 @@ -0,0 +1,212 @@ +ļ»æ#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Build the SRT static libraries installer for Windows. + + .PARAMETER Version + + Use the specified string as version number from libsrt. By default, if + the current commit has a tag, use that tag (initial 'v' removed). Otherwise, + the defaut version is a detailed version number (most recent version, number + of commits since then, short commit SHA). + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. + +#> +[CmdletBinding()] +param( + [string]$Version = "", + [switch]$NoPause = $false +) +Write-Output "Building the SRT static libraries installer for Windows" + +# Directory containing this script: +$ScriptDir = $PSScriptRoot + +# The root of the srt repository is two levels up. +$RepoDir = (Split-Path -Parent (Split-Path -Parent $ScriptDir)) + +# Output directory for final installers: +$OutDir = "$ScriptDir\installers" + +# Temporary directory for build operations: +$TmpDir = "$ScriptDir\tmp" + + +#----------------------------------------------------------------------------- +# A function to exit this script with optional error message, using -NoPause +#----------------------------------------------------------------------------- + +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + + +#----------------------------------------------------------------------------- +# Build SRT version strings +#----------------------------------------------------------------------------- + +# By default, let git format a decent version number. +if (-not $Version) { + $Version = (git describe --tags ) -replace '-g','-' +} +$Version = $Version -replace '^v','' + +# Split version string in pieces and make sure to get at least four elements. +$VField = ($Version -split "[-\. ]") + @("0", "0", "0", "0") | Select-String -Pattern '^\d*$' +$VersionInfo = "$($VField[0]).$($VField[1]).$($VField[2]).$($VField[3])" + +Write-Output "SRT version: $Version" +Write-Output "Windows version info: $VersionInfo" + + +#----------------------------------------------------------------------------- +# Initialization phase, verify prerequisites +#----------------------------------------------------------------------------- + +# Locate OpenSSL root from local installation. +$SslRoot = @{ + "x64" = "C:\Program Files\OpenSSL-Win64"; + "Win32" = "C:\Program Files (x86)\OpenSSL-Win32" +} + +# Verify OpenSSL directories. +$Missing = 0 +foreach ($file in @($SslRoot["x64"], $SslRoot["Win32"])) { + if (-not (Test-Path $file)) { + Write-Output "**** Missing $file" + $Missing = $Missing + 1 + } +} +if ($Missing -gt 0) { + Exit-Script "Missing $Missing OpenSSL files, use install-openssl.ps1 to install OpenSSL" +} + +# Locate MSBuild and CMake, regardless of Visual Studio version. +Write-Output "Searching MSBuild ..." +$MSRoots = @("C:\Program Files*\MSBuild", "C:\Program Files*\Microsoft Visual Studio", "C:\Program Files*\CMake*") +$MSBuild = Get-ChildItem -Recurse -Path $MSRoots -Include MSBuild.exe -ErrorAction Ignore | + ForEach-Object { (Get-Command $_).FileVersionInfo } | + Sort-Object -Unique -Property FileVersion | + ForEach-Object { $_.FileName} | + Select-Object -Last 1 +if (-not $MSBuild) { + Exit-Script "MSBuild not found" +} + +Write-Output "Searching CMake ..." +$CMake = Get-ChildItem -Recurse -Path $MSRoots -Include cmake.exe -ErrorAction Ignore | + ForEach-Object { (Get-Command $_).FileVersionInfo } | + Sort-Object -Unique -Property FileVersion | + ForEach-Object { $_.FileName} | + Select-Object -Last 1 +if (-not $CMake) { + Exit-Script "CMake not found, check option 'C++ CMake tools for Windows' in Visual Studio installer" +} + +# Locate NSIS, the Nullsoft Scriptable Installation System. +Write-Output "Searching NSIS ..." +$NSIS = Get-Item "C:\Program Files*\NSIS\makensis.exe" | ForEach-Object { $_.FullName} | Select-Object -Last 1 +if (-not $NSIS) { + Exit-Script "NSIS not found, use install-nsis.ps1 to install NSIS" +} + +Write-Output "MSBuild: $MSBuild" +Write-Output "CMake: $CMake" +Write-Output "NSIS: $NSIS" + +# Create the directories for builds when necessary. +[void](New-Item -Path $TmpDir -ItemType Directory -Force) +[void](New-Item -Path $OutDir -ItemType Directory -Force) + + +#----------------------------------------------------------------------------- +# Configure and build SRT library using CMake on two architectures. +#----------------------------------------------------------------------------- + +foreach ($Platform in @("x64", "Win32")) { + + # Build directory. Cleanup to force a fresh cmake config. + $BuildDir = "$TmpDir\build.$Platform" + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir + [void](New-Item -Path $BuildDir -ItemType Directory -Force) + + # Run CMake. + Write-Output "Configuring build for platform $Platform ..." + $SRoot = $SslRoot[$Platform] + & $CMake -S $RepoDir -B $BuildDir -A $Platform ` + -DENABLE_STDCXX_SYNC=ON ` + -DOPENSSL_ROOT_DIR="$SRoot" ` + -DOPENSSL_LIBRARIES="$SRoot\lib\libssl_static.lib;$SRoot\lib\libcrypto_static.lib" ` + -DOPENSSL_INCLUDE_DIR="$SRoot\include" + + # Patch version string in version.h + Get-Content "$BuildDir\version.h" | + ForEach-Object { + $_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`"" + } | + Out-File "$BuildDir\version.new" -Encoding ascii + Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force + + # Compile SRT. + Write-Output "Building for platform $Platform ..." + foreach ($Conf in @("Release", "Debug")) { + & $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static + } +} + +# Verify the presence of compiled libraries. +Write-Output "Checking compiled libraries ..." +$Missing = 0 +foreach ($Conf in @("Release", "Debug")) { + foreach ($Platform in @("x64", "Win32")) { + $Path = "$TmpDir\build.$Platform\$Conf\srt_static.lib" + if (-not (Test-Path $Path)) { + Write-Output "**** Missing $Path" + $Missing = $Missing + 1 + } + } +} +if ($Missing -gt 0) { + Exit-Script "Missing $Missing files" +} + + +#----------------------------------------------------------------------------- +# Build the binary installer. +#----------------------------------------------------------------------------- + +Write-Output "Building installer ..." +& $NSIS /V2 ` + /DVersion="$Version" ` + /DVersionInfo="$VersionInfo" ` + /DOutDir="$OutDir" ` + /DBuildRoot="$TmpDir" ` + /DRepoDir="$RepoDir" ` + "$ScriptDir\libsrt.nsi" + +Exit-Script diff --git a/scripts/win-installer/install-nsis.ps1 b/scripts/win-installer/install-nsis.ps1 new file mode 100644 index 000000000..14879b521 --- /dev/null +++ b/scripts/win-installer/install-nsis.ps1 @@ -0,0 +1,122 @@ +ļ»æ#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Download, expand and install NSIS, the NullSoft Installer Scripting. + + .PARAMETER ForceDownload + + Force a download even if NSIS is already downloaded. + + .PARAMETER NoInstall + + Do not install the NSIS package. By default, NSIS is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [switch]$ForceDownload = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "NSIS download and installation procedure" +$NSISPage = "https://nsis.sourceforge.io/Download" +$FallbackURL = "http://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download" + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Local file names. +$RootDir = $PSScriptRoot +$TmpDir = "$RootDir\tmp" + +# Create the directory for external products when necessary. +[void] (New-Item -Path $TmpDir -ItemType Directory -Force) + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the HTML page for NSIS downloads. +$status = 0 +$message = "" +$Ref = $null +try { + $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $NSISPage + $status = [int] [Math]::Floor($response.StatusCode / 100) +} +catch { + $message = $_.Exception.Message +} + +if ($status -ne 1 -and $status -ne 2) { + # Error fetch NSIS download page. + if ($message -eq "" -and (Test-Path variable:response)) { + Write-Output "Status code $($response.StatusCode), $($response.StatusDescription)" + } + else { + Write-Output "#### Error accessing ${NSISPage}: $message" + } +} +else { + # Parse HTML page to locate the latest installer. + $Ref = $response.Links.href | Where-Object { $_ -like "*/nsis-*-setup.exe?download" } | Select-Object -First 1 +} + +if (-not $Ref) { + # Could not find a reference to NSIS installer. + $Url = [System.Uri]$FallbackURL +} +else { + # Build the absolute URL's from base URL (the download page) and href links. + $Url = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$NSISPage, $Ref) +} + +$InstallerName = (Split-Path -Leaf $Url.LocalPath) +$InstallerPath = "$TmpDir\$InstallerName" + +# Download installer +if (-not $ForceDownload -and (Test-Path $InstallerPath)) { + Write-Output "$InstallerName already downloaded, use -ForceDownload to download again" +} +else { + Write-Output "Downloading $Url ..." + Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $InstallerPath + if (-not (Test-Path $InstallerPath)) { + Exit-Script "$Url download failed" + } +} + +# Install NSIS +if (-not $NoInstall) { + Write-Output "Installing $InstallerName" + Start-Process -FilePath $InstallerPath -ArgumentList @("/S") -Wait +} + +Exit-Script diff --git a/scripts/win-installer/install-openssl.ps1 b/scripts/win-installer/install-openssl.ps1 new file mode 100644 index 000000000..83b59954f --- /dev/null +++ b/scripts/win-installer/install-openssl.ps1 @@ -0,0 +1,119 @@ +ļ»æ#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Download, expand and install OpenSSL for Windows. + + .PARAMETER ForceDownload + + Force a download even if the OpenSSL installers are already downloaded. + + .PARAMETER NoInstall + + Do not install the OpenSSL packages. By default, OpenSSL is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [switch]$ForceDownload = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "OpenSSL download and installation procedure" +$OpenSSLHomePage = "http://slproweb.com/products/Win32OpenSSL.html" + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Local file names. +$RootDir = $PSScriptRoot +$TmpDir = "$RootDir\tmp" + +# Create the directory for external products when necessary. +[void] (New-Item -Path $TmpDir -ItemType Directory -Force) + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the HTML page for OpenSSL downloads. +$status = 0 +$message = "" +try { + $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $OpenSSLHomePage + $status = [int] [Math]::Floor($response.StatusCode / 100) +} +catch { + $message = $_.Exception.Message +} +if ($status -ne 1 -and $status -ne 2) { + if ($message -eq "" -and (Test-Path variable:response)) { + Exit-Script "Status code $($response.StatusCode), $($response.StatusDescription)" + } + else { + Exit-Script "#### Error accessing ${OpenSSLHomePage}: $message" + } +} + +# Parse HTML page to locate the latest MSI files. +$Ref32 = $response.Links.href | Where-Object { $_ -like "*/Win32OpenSSL-*.msi" } | Select-Object -First 1 +$Ref64 = $response.Links.href | Where-Object { $_ -like "*/Win64OpenSSL-*.msi" } | Select-Object -First 1 + +# Build the absolute URL's from base URL (the download page) and href links. +$Url32 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref32) +$Url64 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref64) + +# Download and install one MSI package. +function Download-Install([string]$Url) +{ + $MsiName = (Split-Path -Leaf $Url.toString()) + $MsiPath = "$TmpDir\$MsiName" + + if (-not $ForceDownload -and (Test-Path $MsiPath)) { + Write-Output "$MsiName already downloaded, use -ForceDownload to download again" + } + else { + Write-Output "Downloading $Url ..." + Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $MsiPath + } + + if (-not (Test-Path $MsiPath)) { + Exit-Script "$Url download failed" + } + + if (-not $NoInstall) { + Write-Output "Installing $MsiName" + Start-Process msiexec.exe -ArgumentList @("/i", $MsiPath, "/qn", "/norestart") -Wait + } +} + +# Download and install the two MSI packages. +Download-Install $Url32 +Download-Install $Url64 +Exit-Script diff --git a/scripts/win-installer/libsrt.nsi b/scripts/win-installer/libsrt.nsi new file mode 100644 index 000000000..4628642cc --- /dev/null +++ b/scripts/win-installer/libsrt.nsi @@ -0,0 +1,217 @@ +;----------------------------------------------------------------------------- +; +; SRT - Secure, Reliable, Transport +; Copyright (c) 2021, Thierry Lelegard +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +;----------------------------------------------------------------------------- +; +; NSIS script to build the SRT binary installer for Windows. +; Do not invoke NSIS directly, use PowerShell script build-win-installer.ps1 +; to ensure that all parameters are properly passed. +; +;----------------------------------------------------------------------------- + +Name "SRT" +Caption "SRT Libraries Installer" + +!verbose push +!verbose 0 +!include "MUI2.nsh" +!include "Sections.nsh" +!include "TextFunc.nsh" +!include "FileFunc.nsh" +!include "WinMessages.nsh" +!include "x64.nsh" +!verbose pop + +!define ProductName "libsrt" +!define Build32Dir "${BuildRoot}\build.Win32" +!define Build64Dir "${BuildRoot}\build.x64" +!define SSL32Dir "C:\Program Files (x86)\OpenSSL-Win32" +!define SSL64Dir "C:\Program Files\OpenSSL-Win64" + +; Installer file information. +VIProductVersion ${VersionInfo} +VIAddVersionKey ProductName "${ProductName}" +VIAddVersionKey ProductVersion "${Version}" +VIAddVersionKey Comments "The SRT static libraries for Visual C++ on Windows" +VIAddVersionKey CompanyName "Haivision" +VIAddVersionKey LegalCopyright "Copyright (c) 2021 Haivision Systems Inc." +VIAddVersionKey FileVersion "${VersionInfo}" +VIAddVersionKey FileDescription "SRT Installer" + +; Name of binary installer file. +OutFile "${OutDir}\${ProductName}-${Version}.exe" + +; Generate a Unicode installer (default is ANSI). +Unicode true + +; Registry key for environment variables +!define EnvironmentKey '"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +; Registry entry for product info and uninstallation info. +!define ProductKey "Software\${ProductName}" +!define UninstallKey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ProductName}" + +; Use XP manifest. +XPStyle on + +; Request administrator privileges for Windows Vista and higher. +RequestExecutionLevel admin + +; "Modern User Interface" (MUI) settings. +!define MUI_ABORTWARNING + +; Default installation folder. +InstallDir "$PROGRAMFILES\${ProductName}" + +; Get installation folder from registry if available from a previous installation. +InstallDirRegKey HKLM "${ProductKey}" "InstallDir" + +; Installer pages. +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +; Uninstaller pages. +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +; Languages. +!insertmacro MUI_LANGUAGE "English" + +; Installation initialization. +function .onInit + ; In 64-bit installers, don't use registry redirection. + ${If} ${RunningX64} + SetRegView 64 + ${EndIf} +functionEnd + +; Uninstallation initialization. +function un.onInit + ; In 64-bit installers, don't use registry redirection. + ${If} ${RunningX64} + SetRegView 64 + ${EndIf} +functionEnd + +; Installation section +Section "Install" + + ; Work on "all users" context, not current user. + SetShellVarContext all + + ; Delete obsolete files from previous versions. + Delete "$INSTDIR\LICENSE.pthread.txt" + Delete "$INSTDIR\include\srt\srt4udt.h" + Delete "$INSTDIR\include\srt\udt.h" + Delete "$INSTDIR\lib\Release-x64\pthread.lib" + Delete "$INSTDIR\lib\Release-Win32\pthread.lib" + Delete "$INSTDIR\lib\Debug-x64\srt.pdb" + Delete "$INSTDIR\lib\Debug-x64\pthread.pdb" + Delete "$INSTDIR\lib\Debug-x64\pthread.lib" + Delete "$INSTDIR\lib\Debug-Win32\srt.pdb" + Delete "$INSTDIR\lib\Debug-Win32\pthread.pdb" + Delete "$INSTDIR\lib\Debug-Win32\pthread.lib" + + SetOutPath "$INSTDIR" + File /oname=LICENSE.txt "${RepoDir}\LICENSE" + File "libsrt.props" + + ; Header files. + CreateDirectory "$INSTDIR\include\srt" + SetOutPath "$INSTDIR\include\srt" + File "${RepoDir}\srtcore\logging_api.h" + File "${RepoDir}\srtcore\platform_sys.h" + File "${RepoDir}\srtcore\srt.h" + File "${Build64Dir}\version.h" + + CreateDirectory "$INSTDIR\include\win" + SetOutPath "$INSTDIR\include\win" + File "${RepoDir}\common\win\syslog_defs.h" + + ; Libraries. + CreateDirectory "$INSTDIR\lib" + + CreateDirectory "$INSTDIR\lib\Release-x64" + SetOutPath "$INSTDIR\lib\Release-x64" + File /oname=srt.lib "${Build64Dir}\Release\srt_static.lib" + File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MD.lib" + File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MD.lib" + + CreateDirectory "$INSTDIR\lib\Debug-x64" + SetOutPath "$INSTDIR\lib\Debug-x64" + File /oname=srt.lib "${Build64Dir}\Debug\srt_static.lib" + File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MDd.lib" + File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MDd.lib" + + CreateDirectory "$INSTDIR\lib\Release-Win32" + SetOutPath "$INSTDIR\lib\Release-Win32" + File /oname=srt.lib "${Build32Dir}\Release\srt_static.lib" + File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MD.lib" + File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MD.lib" + + CreateDirectory "$INSTDIR\lib\Debug-Win32" + SetOutPath "$INSTDIR\lib\Debug-Win32" + File /oname=srt.lib "${Build32Dir}\Debug\srt_static.lib" + File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MDd.lib" + File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MDd.lib" + + ; Add an environment variable to installation root. + WriteRegStr HKLM ${EnvironmentKey} "LIBSRT" "$INSTDIR" + + ; Store installation folder in registry. + WriteRegStr HKLM "${ProductKey}" "InstallDir" $INSTDIR + + ; Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + + ; Declare uninstaller in "Add/Remove Software" control panel + WriteRegStr HKLM "${UninstallKey}" "DisplayName" "${ProductName}" + WriteRegStr HKLM "${UninstallKey}" "Publisher" "Haivision" + WriteRegStr HKLM "${UninstallKey}" "URLInfoAbout" "https://github.com/Haivision/srt" + WriteRegStr HKLM "${UninstallKey}" "DisplayVersion" "${Version}" + WriteRegStr HKLM "${UninstallKey}" "DisplayIcon" "$INSTDIR\Uninstall.exe" + WriteRegStr HKLM "${UninstallKey}" "UninstallString" "$INSTDIR\Uninstall.exe" + + ; Get estimated size of installed files + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKLM "${UninstallKey}" "EstimatedSize" "$0" + + ; Notify applications of environment modifications + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +SectionEnd + +; Uninstallation section +Section "Uninstall" + + ; Work on "all users" context, not current user. + SetShellVarContext all + + ; Get installation folder from registry + ReadRegStr $0 HKLM "${ProductKey}" "InstallDir" + + ; Delete product registry entries + DeleteRegKey HKCU "${ProductKey}" + DeleteRegKey HKLM "${ProductKey}" + DeleteRegKey HKLM "${UninstallKey}" + DeleteRegValue HKLM ${EnvironmentKey} "LIBSRT" + + ; Delete product files. + RMDir /r "$0\include" + RMDir /r "$0\lib" + Delete "$0\libsrt.props" + Delete "$0\LICENSE*" + Delete "$0\Uninstall.exe" + RMDir "$0" + + ; Notify applications of environment modifications + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +SectionEnd diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props new file mode 100644 index 000000000..d8de447c1 --- /dev/null +++ b/scripts/win-installer/libsrt.props @@ -0,0 +1,19 @@ + + + + + + + + + + $(LIBSRT)\include;%(AdditionalIncludeDirectories) + + + srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) + $(LIBSRT)\lib\$(Configuration)-$(Platform);%(AdditionalLibraryDirectories) + /ignore:4099 %(AdditionalOptions) + + + + From 36f89952037c3715ed7cbfe7d98d89995ed591cf Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 13:46:00 +0200 Subject: [PATCH 173/790] [core] CRendezvousQueue: added doxygen comments. Renamed m_RIDVectorLock to m_RIDListLock. --- srtcore/queue.cpp | 20 ++++++++++---------- srtcore/queue.h | 47 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8fe5809bc..2de1515dd 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -815,7 +815,7 @@ void CHash::remove(int32_t id) // CRendezvousQueue::CRendezvousQueue() : m_lRendezvousID() - , m_RIDVectorLock() + , m_RIDListLock() { } @@ -827,7 +827,7 @@ CRendezvousQueue::~CRendezvousQueue() void CRendezvousQueue::insert( const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); CRL r; r.m_iID = id; @@ -843,7 +843,7 @@ void CRendezvousQueue::insert( void CRendezvousQueue::remove(const SRTSOCKET &id) { - ScopedLock lkv (m_RIDVectorLock); + ScopedLock lkv (m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { @@ -855,12 +855,12 @@ void CRendezvousQueue::remove(const SRTSOCKET &id) } } -CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) +CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); // TODO: optimize search - for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) + for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) { @@ -915,7 +915,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con #endif { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); if (m_lRendezvousID.empty()) return; @@ -1004,7 +1004,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con } // The call to completeBrokenConnectionDependencies() cannot happen here - // under the lock of m_RIDVectorLock as it risks a deadlock. Collect it + // under the lock of m_RIDListLock as it risks a deadlock. Collect it // to update later. LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; ufailed.push_back(fi); @@ -1026,7 +1026,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { IF_HEAVY_LOGGING(++debug_nupd); - // Collect them so that they can be updated out of m_RIDVectorLock. + // Collect them so that they can be updated out of m_RIDListLock. LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; uprocess.push_back(fi); // NOTE: safe loop, the incrementation was done before the loop body, @@ -1117,7 +1117,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { // Now, additionally for every failed link reset the TTL so that // they are set expired right now. - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (find_if(ufailed.begin(), ufailed.end(), LinkStatusInfo::HasID(i->m_iID)) != ufailed.end()) diff --git a/srtcore/queue.h b/srtcore/queue.h index 2d27c67b9..d59877983 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -320,32 +320,53 @@ class CHash CHash& operator=(const CHash&); }; +/// @brief A queue of sockets pending for connection. +/// It can be either a caller socket in a non-blocking mode +/// (the connection has to be handled in background), +/// or a socket in rendezvous connection mode. class CRendezvousQueue { public: - CRendezvousQueue(); - ~CRendezvousQueue(); + CRendezvousQueue(); + ~CRendezvousQueue(); public: - void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, - const srt::sync::steady_clock::time_point &ttl); - - void remove(const SRTSOCKET& id); - CUDT* retrieve(const sockaddr_any& addr, SRTSOCKET& id); - - void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response); + /// @brief Insert a new socket pending for connection (non-blocking caller or rendezvous). + /// @param id socket ID. + /// @param u pointer to a corresponding CUDT instance. + /// @param addr remote address to connect to. + /// @param ttl timepoint for connection attempt to expire. + void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, + const srt::sync::steady_clock::time_point &ttl); + + /// @brief Remove a socket from the connection pending list. + /// @param id socket ID. + void remove(const SRTSOCKET& id); + + /// @brief Locate a socket in the connection pending queue. + /// @param addr source address of the packet received over UDP (peer address). + /// @param id socket ID. + /// @return a pointer to CUDT instance retrieved, or NULL if nothing was found. + CUDT* retrieve(const sockaddr_any& addr, SRTSOCKET& id) const; + + /// @brief Update status of connections in the pending queue. + /// Stop connecting if TTL expires. Resend handshake request every 250 ms if no response from the peer. + /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. + /// @param cst target status for pending connection: reject or proceed. + /// @param response packet received from the UDP socket. + void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& response); private: struct CRL { - SRTSOCKET m_iID; // UDT socket ID (self) - CUDT* m_pUDT; // UDT instance - sockaddr_any m_PeerAddr;// UDT sonnection peer address + SRTSOCKET m_iID; // SRT socket ID (self) + CUDT* m_pUDT; // CUDT instance + sockaddr_any m_PeerAddr;// SRT sonnection peer address srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires }; std::list m_lRendezvousID; // The sockets currently in rendezvous mode - srt::sync::Mutex m_RIDVectorLock; + mutable srt::sync::Mutex m_RIDListLock; }; class CSndQueue From 917a71513e3d90e7c2c339c7a63f8f38b7af6e16 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 15:23:52 +0200 Subject: [PATCH 174/790] [core] Added CRendezvousQueue::qualifyToHandle. Now TTL of a socket pending for connection is checked on every update iteration -> more precise connection timeout. --- srtcore/queue.cpp | 276 +++++++++++++++++++--------------------------- srtcore/queue.h | 36 +++++- 2 files changed, 145 insertions(+), 167 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 2de1515dd..c2aa8d40d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -885,168 +885,22 @@ CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) cons return NULL; } -struct LinkStatusInfo +void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &pktIn) { - CUDT* u; - SRTSOCKET id; - int errorcode; - sockaddr_any peeraddr; - int token; + vector toRemove, toProcess; - struct HasID - { - SRTSOCKET id; - HasID(SRTSOCKET p): id(p) {} - bool operator()(const LinkStatusInfo& i) - { - return i.id == id; - } - }; -}; - -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &response) -{ - vector ufailed, uprocess; - -#if ENABLE_HEAVY_LOGGING - int debug_nupd = 0; - int debug_nrun = 0; - int debug_nfail = 0; -#endif - - { - ScopedLock vg(m_RIDListLock); - - if (m_lRendezvousID.empty()) - return; - - HLOGC(cnlog.Debug, - log << "updateConnStatus: updating after getting pkt id=" << response.m_iID - << " status: " << ConnectStatusStr(cst)); - - for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) - { - ++i_next; - // NOTE: This is a SAFE LOOP. - // Incrementation will be done at the end, after the processing did not - // REMOVE the currently processed element. When the element was removed, - // the iterator value for the next iteration will be taken from erase()'s result. - - // RST_AGAIN happens in case when the last attempt to read a packet from the UDP - // socket has read nothing. In this case it would be a repeated update, while - // still waiting for a response from the peer. When we have any other state here - // (most expectably CONN_CONTINUE or CONN_RENDEZVOUS, which means that a packet has - // just arrived in this iteration), do the update immetiately (in SRT this also - // involves additional incoming data interpretation, which wasn't the case in UDT). - - // Use "slow" cyclic responding in case when - // - RST_AGAIN (no packet was received for whichever socket) - // - a packet was received, but not for THIS socket - if (rst == RST_AGAIN || i->m_iID != response.m_iID) - { - // If no packet has been received from the peer, - // avoid sending too many requests, at most 1 request per 250ms - const steady_clock::time_point then = i->m_pUDT->m_tsLastReqTime; - const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration timeout_250ms = milliseconds_from(250); - const bool now_is_time = (now - then) > timeout_250ms; - HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << " then=" << FormatTime(then) - << " now=" << FormatTime(now) << " passed=" << count_microseconds(now - then) - << "<=> 250000 -- now's " << (now_is_time ? "" : "NOT ") << "the time"); - - if (!now_is_time) - continue; - } - - HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- sending update NOW."); - -#if ENABLE_HEAVY_LOGGING - ++debug_nrun; -#endif - - // XXX This looks like a loop that rolls in infinity without any sleeps - // inside and makes it once per about 50 calls send a hs conclusion - // for a randomly sampled rendezvous ID of a socket out of the list. - // Ok, probably the rendezvous ID should be just one so not much to - // sample from, but if so, why the container? - // - // This must be somehow fixed! - // - // Maybe the time should be simply checked once and the whole loop not - // done when "it's not the time"? - const steady_clock::time_point now = steady_clock::now(); - if (now >= i->m_tsTTL) - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID - << " removed - EXPIRED (" - // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. - << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") - << "). WILL REMOVE from queue"); - - // Set appropriate error information, but do not update yet. - // Exit the lock first. Collect objects to update them later. - int ccerror = SRT_ECONNREJ; - if (i->m_pUDT->m_RejectReason == SRT_REJ_UNKNOWN) - { - if (!is_zero(i->m_tsTTL)) - { - // Timer expired, set TIMEOUT forcefully - i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; - ccerror = SRT_ENOSERVER; - } - else - { - // In case of unknown reason, rejection should at least - // suggest error on the peer - i->m_pUDT->m_RejectReason = SRT_REJ_PEER; - } - } - - // The call to completeBrokenConnectionDependencies() cannot happen here - // under the lock of m_RIDListLock as it risks a deadlock. Collect it - // to update later. - LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; - ufailed.push_back(fi); - - // i_next was preincremented, but this is guaranteed to point to - // the element next to erased one. - i_next = m_lRendezvousID.erase(i); - continue; - } - else - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << std::fixed << (count_microseconds(i->m_tsTTL - now)/1000000.0) << "s of TTL)..."); - } - - // This queue is used only in case of Async mode (rendezvous or caller-listener). - // Synchronous connection requests are handled in startConnect() completely. - if (!i->m_pUDT->m_config.bSynRecving) - { - IF_HEAVY_LOGGING(++debug_nupd); - - // Collect them so that they can be updated out of m_RIDListLock. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; - uprocess.push_back(fi); - // NOTE: safe loop, the incrementation was done before the loop body, - // so the `i' node can be safely deleted. Just the body must end here. - continue; - } - else - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " deemed SYNCHRONOUS, NOT UPDATING"); - } - } - - } + // If no socket were qualified for further handling, finish here. + // Otherwise toRemove and toProcess contain items to handle. + if (!qualifyToHandle(rst, cst, pktIn.m_iID, toRemove, toProcess)) + return; // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << uprocess.size() << " for processing, " - << ufailed.size() << " to close"); + HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " + << toRemove.size() << " to close"); - for (vector::iterator i = uprocess.begin(); i != uprocess.end(); ++i) + // Repeat (resend) connection request. + for (vector::iterator i = toProcess.begin(); i != toProcess.end(); ++i) { // IMPORTANT INFORMATION concerning changes towards UDT legacy. // In the UDT code there was no attempt to interpret any incoming data. @@ -1054,7 +908,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // m_ConnRes field, and m_ConnReq field was considered at this time accordingly updated. // Therefore this procedure did only one thing: craft a new handshake packet and send it. // In SRT this may also interpret extra data (extensions in case when Agent is Responder) - // and the `response` packet may sometimes contain no data. Therefore the passed `rst` + // and the `pktIn` packet may sometimes contain no data. Therefore the passed `rst` // must be checked to distinguish the call by periodic update (RST_AGAIN) from a call // due to have received the packet (RST_OK). // @@ -1065,7 +919,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (i->id != response.m_iID) + if (i->id != pktIn.m_iID) { read_st = RST_AGAIN; conn_st = CONN_AGAIN; @@ -1073,15 +927,14 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); - if (!i->u->processAsyncConnectRequest(read_st, conn_st, response, i->peeraddr)) + if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. LinkStatusInfo fi = *i; fi.errorcode = SRT_ECONNREJ; - ufailed.push_back(fi); + toRemove.push_back(fi); i->u->sendCtrl(UMSG_SHUTDOWN); - IF_HEAVY_LOGGING(++debug_nfail); } } @@ -1092,7 +945,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // they are moved to ClosedSockets and it is believed that this function will // not be held on mutexes that long. - for (vector::iterator i = ufailed.begin(); i != ufailed.end(); ++i) + for (vector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); /* @@ -1120,17 +973,110 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con ScopedLock vg(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { - if (find_if(ufailed.begin(), ufailed.end(), LinkStatusInfo::HasID(i->m_iID)) != ufailed.end()) + if (find_if(toRemove.begin(), toRemove.end(), LinkStatusInfo::HasID(i->m_iID)) != toRemove.end()) { LOGC(cnlog.Error, log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID << ". Setting TTL as EXPIRED."); i->m_tsTTL = steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration } } } +} + +bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, + int iDstSockID, vector& toRemove, vector& toProcess) +{ + ScopedLock vg(m_RIDListLock); + + if (m_lRendezvousID.empty()) + return false; // nothing to process. HLOGC(cnlog.Debug, - log << "updateConnStatus: " << debug_nupd << "/" << debug_nrun << " sockets updated (" - << (debug_nrun - debug_nupd) << " useless). REMOVED " << debug_nfail << " sockets."); + log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID + << " status: " << ConnectStatusStr(cst)); + + for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) + { + // Safe iterator to the next element. If the current element is erased, the iterator is updated again. + ++i_next; + + const steady_clock::time_point tsNow = steady_clock::now(); + + if (tsNow >= i->m_tsTTL) + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID + << " removed - EXPIRED (" + // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. + << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") + << "). WILL REMOVE from queue."); + + // Set appropriate error information, but do not update yet. + // Exit the lock first. Collect objects to update them later. + int ccerror = SRT_ECONNREJ; + if (i->m_pUDT->m_RejectReason == SRT_REJ_UNKNOWN) + { + if (!is_zero(i->m_tsTTL)) + { + // Timer expired, set TIMEOUT forcefully + i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; + ccerror = SRT_ENOSERVER; + } + else + { + // In case of unknown reason, rejection should at least + // suggest error on the peer + i->m_pUDT->m_RejectReason = SRT_REJ_PEER; + } + } + + // The call to completeBrokenConnectionDependencies() cannot happen here + // under the lock of m_RIDListLock as it risks a deadlock. + // Collect in 'toRemove' to update later. + LinkStatusInfo fi = { i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1 }; + toRemove.push_back(fi); + + // i_next was preincremented, but this is guaranteed to point to + // the element next to erased one. + i_next = m_lRendezvousID.erase(i); + continue; + } + else + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " + << std::fixed << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + } + + const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; + const steady_clock::time_point tsRepeat = tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). + + // A connection request is repeated every 250 ms if there was no response from the peer: + // - RST_AGAIN means no packet was received over UDP. + // - a packet was received, but not for THIS socket. + 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."); + + continue; + } + + HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); + + // This queue is used only in case of Async mode (rendezvous or caller-listener). + // Synchronous connection requests are handled in startConnect() completely. + if (!i->m_pUDT->m_config.bSynRecving) + { + // Collect them so that they can be updated out of m_RIDListLock. + LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1 }; + toProcess.push_back(fi); + } + else + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " is SYNCHRONOUS, NOT UPDATING"); + } + } + + return !toRemove.empty() || !toProcess.empty(); } // diff --git a/srtcore/queue.h b/srtcore/queue.h index d59877983..9c0affd5e 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -353,8 +353,40 @@ class CRendezvousQueue /// Stop connecting if TTL expires. Resend handshake request every 250 ms if no response from the peer. /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. - /// @param response packet received from the UDP socket. - void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& response); + /// @param pktIn packet received from the UDP socket. + void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn); + +private: + struct LinkStatusInfo + { + CUDT* u; + SRTSOCKET id; + int errorcode; + sockaddr_any peeraddr; + int token; + + struct HasID + { + SRTSOCKET id; + HasID(SRTSOCKET p) : id(p) {} + bool operator()(const LinkStatusInfo& i) + { + return i.id == id; + } + }; + }; + + /// @brief Qualify pending connections: + /// - Sockets with expired TTL go to the 'to_remove' list and removed from the queue straight away. + /// - If HS request is to be resent (resend 250 ms if no response from the peer) go to the 'to_process' list. + /// + /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. + /// @param cst target status for pending connection: reject or proceed. + /// @param iDstSockID destination socket ID of the received packet. + /// @param[in,out] toRemove stores sockets with expired TTL. + /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. + bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, int iDstSockID, + std::vector& toRemove, std::vector& toProcess); private: struct CRL From 87746453edcd37f2ae1987cfd052ca8f447c8738 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 15:25:16 +0200 Subject: [PATCH 175/790] [tests] ConnectionTimeout.Nonblocking 500ms -> 300ms. Previously TTL was checked only every 250 ms, which resulted in a poor timeout precision. --- test/test_connection_timeout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 8b9bd31f8..241c5f1a8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -100,7 +100,7 @@ TEST_F(TestConnectionTimeout, Nonblocking) { EXPECT_EQ(conn_timeout, 3000); // Set connection timeout to 500 ms to reduce the test execution time - const int connection_timeout_ms = 500; + const int connection_timeout_ms = 300; EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); const int yes = 1; From 0921da6cfd6e7c0f88e0a0400f05ded660af1e07 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 21 Apr 2021 16:24:13 +0200 Subject: [PATCH 176/790] [core] Fix DriftTracer: take RTT into account --- srtcore/buffer.cpp | 3 +- srtcore/buffer.h | 1 + srtcore/core.cpp | 2 +- srtcore/tsbpd_time.cpp | 107 +++++++++++++++++++++++++++++++++++++++-- srtcore/tsbpd_time.h | 12 +++-- srtcore/utilities.h | 6 ++- 6 files changed, 121 insertions(+), 10 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e2efcefff..b29aa9322 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1833,10 +1833,11 @@ void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, + int rtt, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { - return m_tsbpd.addDriftSample(timestamp_us, w_udrift, w_newtimebase); + return m_tsbpd.addDriftSample(timestamp_us, rtt, w_udrift, w_newtimebase); } int CRcvBuffer::readMsg(char* data, int len) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index d32e1d29e..7f7680b8d 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -414,6 +414,7 @@ class CRcvBuffer /// @param [out] w_udrift current drift value /// @param [out] w_newtimebase current TSBPD base time bool addRcvTsbPdDriftSample(uint32_t timestamp, + int rtt, duration& w_udrift, time_point& w_newtimebase); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index efc866164..a1985379d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8279,7 +8279,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival steady_clock::duration udrift(0); steady_clock::time_point newtimebase; const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), - (udrift), (newtimebase)); + rtt, (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 0fe07fb7c..8fedf82ea 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -19,17 +19,110 @@ using namespace srt::sync; namespace srt { +#if SRT_DEBUG_TRACE_DRIFT +class drift_logger +{ + using steady_clock = srt::sync::steady_clock; + +public: + drift_logger() {} + + ~drift_logger() + { + ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(unsigned ackack_timestamp, + int rtt_us, + int64_t drift_sample, + int64_t drift, + int64_t overdrift, + const std::chrono::steady_clock::time_point& tsbpd_base) + { + using namespace srt::sync; + ScopedLock lck(m_mtx); + create_file(); + + // std::string str_tnow = srt::sync::FormatTime(steady_clock::now()); + // str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [STDY]' part + + std::string str_tbase = srt::sync::FormatTime(tsbpd_base); + str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part + + // m_fout << str_tnow << ","; + m_fout << count_microseconds(steady_clock::now() - m_start_time) << ","; + m_fout << ackack_timestamp << ","; + m_fout << rtt_us << ","; + m_fout << drift_sample << ","; + m_fout << drift << ","; + m_fout << overdrift << ","; + m_fout << str_tbase << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "usElapsedStd,usAckAckTimestampStd,"; + m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,TSBPDBase\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + m_start_time = srt::sync::steady_clock::now(); + std::string str_tnow = srt::sync::FormatTimeSys(m_start_time); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) + { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "drift_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; + srt::sync::steady_clock::time_point m_start_time; +}; + +drift_logger g_drift_logger; + +#endif // SRT_DEBUG_TRACE_DRIFT + bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, + int usRTTSample, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { if (!m_bTsbPdMode) return false; - + const time_point tsNow = steady_clock::now(); ScopedLock lck(m_mtxRW); - const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp); + + // Remember the first RTT sample measured. Ideally we need RTT0 - the one from the handshaking phase, + // because TSBPD base is initialized there. But HS-based RTT is not yet implemented. + // Take the first one assuming it is close to RTT0. + if (m_iFirstRTT == -1) + { + m_iFirstRTT = usRTTSample; + } + + // A change in network delay has to be taken into account. The only way to get some estimation of it + // is to estimate RTT change and assume that the change of the one way network delay is + // approximated by the half of the RTT change. + const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); + const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp) - tdRTTDelta; const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); @@ -53,6 +146,14 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, w_udrift = tdDrift; w_newtimebase = m_tsTsbPdTimeBase; +#if SRT_DEBUG_TRACE_DRIFT + g_drift_logger.trace(usPktTimestamp, + usRTTSample, + count_microseconds(tdDrift), + m_DriftTracer.drift(), + m_DriftTracer.overdrift(), + m_tsTsbPdTimeBase); +#endif return updated; } @@ -67,7 +168,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra // // This function is called in the HSREQ reception handler only. m_tsTsbPdTimeBase = timebase; - m_tdTsbPdDelay = delay; + m_tdTsbPdDelay = delay; } void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h index 5dcc7ff3a..b6cb770f5 100644 --- a/srtcore/tsbpd_time.h +++ b/srtcore/tsbpd_time.h @@ -30,7 +30,8 @@ class CTsbpdTime public: CTsbpdTime() - : m_bTsbPdMode(false) + : m_iFirstRTT(-1) + , m_bTsbPdMode(false) , m_tdTsbPdDelay(0) , m_bTsbPdWrapCheck(false) { @@ -61,13 +62,17 @@ class CTsbpdTime /// @brief Add new drift sample from an ACK-ACKACK pair. /// ACKACK packets are sent immediately (except for UDP buffering). + /// Therefore their timestamp roughly corresponds to the time of sending + /// and can be used to estimate clock drift. /// /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. + /// @param [in] usRTTSample RTT sample from an ACK-ACKACK pair. /// @param [out] w_udrift Current clock drift value. /// @param [out] w_newtimebase Current TSBPD base time. /// /// @return true if TSBPD base time has changed, false otherwise. bool addDriftSample(uint32_t pktTimestamp, + int usRTTSample, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase); @@ -116,8 +121,9 @@ class CTsbpdTime void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const; private: - bool m_bTsbPdMode; //< Rreceiver buffering and TSBPD is active when true. - duration m_tdTsbPdDelay; //< Negotiated buffering delay. + int m_iFirstRTT; // First measured RTT sample. + bool m_bTsbPdMode; // Receiver buffering and TSBPD is active when true. + duration m_tdTsbPdDelay; // Negotiated buffering delay. /// @brief Local time base for TsbPd. /// @note m_tsTsbPdTimeBase is changed in the following cases: diff --git a/srtcore/utilities.h b/srtcore/utilities.h index f0ad80271..7d73c8567 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -791,11 +791,13 @@ class DriftTracer m_qDriftSum += driftval; ++m_uDriftSpan; + // I moved it here to calculate accumulated overdrift. + if (CLEAR_ON_UPDATE) + m_qOverdrift = 0; + if (m_uDriftSpan < MAX_SPAN) return false; - if (CLEAR_ON_UPDATE) - m_qOverdrift = 0; // Calculate the median of all drift values. // In most cases, the divisor should be == MAX_SPAN. From a32e975f2403c8b2335e7ab9e77f21291a7e2b3c Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Tue, 18 May 2021 18:57:51 +1000 Subject: [PATCH 177/790] [build] Update for Android build scripts (#2009) - Remove obsolete declares and scripts; - Allow to set build parameters (ndk root, api level, archs set, versions) from the command line; - Use C++11 version of sync module by default. --- docs/build/build-android.md | 24 ++-------- scripts/build-android/README.md | 2 - scripts/build-android/build-android | 69 ++++++++++++++++++++++++++++ scripts/build-android/mkall | 71 ----------------------------- scripts/build-android/mksrt | 24 ++++++++-- scripts/build-android/mkssl | 16 ++----- scripts/build-android/packjni | 38 --------------- scripts/build-android/prepare_build | 36 --------------- 8 files changed, 100 insertions(+), 180 deletions(-) create mode 100755 scripts/build-android/build-android delete mode 100755 scripts/build-android/mkall delete mode 100755 scripts/build-android/packjni delete mode 100644 scripts/build-android/prepare_build diff --git a/docs/build/build-android.md b/docs/build/build-android.md index f2dec839e..af1685a61 100644 --- a/docs/build/build-android.md +++ b/docs/build/build-android.md @@ -2,31 +2,17 @@ **NOTE:** The scripts have been moved to [scripts/build-android](../../scripts/build-android/) folder. -## Establishing a Build Environment - -### Installing the Android NDK +## Install the NDK and CMake The Android NDK is required to build native modules for Android. +[Install and configure the NDK](https://developer.android.com/studio/projects/install-ndk) + Consider installing the latest version of cmake. The higher version of cmake the better. As of writing the current version of CMake is 3.18.4 You can download Cmake from the following website: [https://cmake.org/download](https://cmake.org/download/) -Download the NDK r19 or newer archive from the following site: -[Download the Android NDK on developer.android.com](https://developer.android.com/ndk/downloads/index.html) -To install the Android NDK, simply expand the archive in the folder where you want to install it. - -### OpenSSL - -Google removed openssl from Android 7+. You must build openssl libs by yourself. - -## Configure the NDK Path - -Edit the ```mkall``` script to configure NDK path. Set the ```NDK``` to the directory where the NDK is installed. - ## Build SRT for Android -Run ```/bin/bash mkall > build.log``` script. Libraries will be installed to ```./target-architecture/lib```. - -## Export SRT Libraries +Run ```./build-android -n /path/to/ndk```. E.g. ```./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529``` -Run ```/bin/bash packjni``` to generate ```jniLibs``` archive for Android Studio. +[Include prebuilt native libraries](https://developer.android.com/studio/projects/gradle-external-native-builds#jniLibs) from ```prebuilt``` folder into Android Studio project. diff --git a/scripts/build-android/README.md b/scripts/build-android/README.md index d225ed022..85277f2a6 100644 --- a/scripts/build-android/README.md +++ b/scripts/build-android/README.md @@ -1,5 +1,3 @@ ## Scripts for building SRT for Android -**NOTE:** The scripts have been moved from `docs/Android/` folder. Updating the paths might be required. - See [Building SRT for Android](../../docs/build/build-android.md) for the instructions. diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android new file mode 100755 index 000000000..622f525d2 --- /dev/null +++ b/scripts/build-android/build-android @@ -0,0 +1,69 @@ +#!/bin/sh + +echo_help() +{ + echo "Usage: $0 [options...]" + echo " -n Specify NDK root path for the build." + echo " -a Select target API level." + echo " -t Select target architectures." + echo " Android supports the following architectures: armeabi armeabi-v7a arm64-v8a x86 x86_64." + echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" + echo " -s Select a specific SRT tag. E.g. v1.4.3" + echo + echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" + echo +} + +# Init optional command line vars +NDK_ROOT="" +API_LEVEL=28 +BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" +OPENSSL_VERSION=1.1.1h +SRT_VERSION="" + +while getopts n:a:t:o:s: option +do + case "${option}" + in + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + s) SRT_VERSION=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + +echo_help + +if [ -z "$NDK_ROOT" ] ; then + echo "NDK directory not set." + exit 128 +else + if [ ! -d "$NDK_ROOT" ]; then + echo "NDK directory does not exist: $NDK_ROOT" + exit 128 + fi +fi + +# Determine the path of the executing script +BASE_DIR=$(readlink -f $0 | xargs dirname) + +$BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION + +if [ ! -d $BASE_DIR/srt ]; then + git clone https://github.com/Haivision/srt srt + if [ ! -z "$SRT_VERSION" ]; then + git -C $BASE_DIR/srt checkout $SRT_VERSION + fi +fi + +JNI_DIR=$BASE_DIR/prebuilt + +for build_target in $BUILD_TARGETS; do + git -C $BASE_DIR/srt clean -fd + $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/srt -i $BASE_DIR/$build_target + + mkdir -p $JNI_DIR/$build_target + cp $BASE_DIR/$build_target/lib/libsrt.so $JNI_DIR/$build_target/libsrt.so +done diff --git a/scripts/build-android/mkall b/scripts/build-android/mkall deleted file mode 100755 index dbfe386a3..000000000 --- a/scripts/build-android/mkall +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -NDK=/opt/android-ndk-r19c - -openssl_ver=1.1.1h -#srt_version=1.4.2 -#srt_branch=dev - -API_LEVEL=28 -# Determine the path of the executing script -BASE_DIR=$(readlink -f $0 | xargs dirname) - -#wget -N https://www.openssl.org/source/openssl-$openssl_ver.tar.gz - -if [ ! -d $BASE_DIR/srt ]; then - git clone https://github.com/Haivision/srt srt -# git -C $BASE_DIR/srt checkout v${srt_version} -# git -C $BASE_DIR/srt checkout -b $srt_branch origin/$srt_branch -fi - -# Clang, binutils, the sysroot, and other toolchain pieces are all installed to $NDK/toolchains/llvm/prebuilt/ -toolchain=$NDK/toolchains/llvm/prebuilt/linux-x86_64 - -# Cross-compiler tool prefix -declare -A target_hosts -target_hosts=( - [armeabi]=arm-linux-androideabi - [arm]=arm-linux-androideabi - [arm64]=aarch64-linux-android - [x86]=i686-linux-android - [x86_64]=x86_64-linux-android -) - -# Cross-compiler prefix for -clang and -clang++ has api version -declare -A target_api -target_api=( - [armeabi]=armv5te-linux-androideabi16 - [arm]=armv7a-linux-androideabi16 - [arm64]=aarch64-linux-android21 - [x86]=i686-linux-android16 - [x86_64]=x86_64-linux-android21 -) - -declare -A sysroots -sysroots=( - [armeabi]=$BASE_DIR/armeabi - [arm]=$BASE_DIR/armeabi-v7a - [arm64]=$BASE_DIR/arm64-v8a - [x86]=$BASE_DIR/x86 - [x86_64]=$BASE_DIR/x86_64 -) - -declare -A archabi -archabi=( - [armeabi]=armeabi - [arm]=armeabi-v7a - [arm64]=arm64-v8a - [x86]=x86 - [x86_64]=x86_64 -) - -$BASE_DIR/mkssl -n $NDK -o $openssl_ver -a $API_LEVEL - -for arch in armeabi arm arm64 x86 x86_64; do - #rm -rf $BASE_DIR/openssl-$openssl_ver - #tar xf $BASE_DIR/openssl-$openssl_ver.tar.gz - #$BASE_DIR/mkssl -t $toolchain -h ${target_hosts[$arch]} -a ${target_api[$arch]} -s $BASE_DIR/openssl-$openssl_ver -i ${sysroots[$arch]} - - git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -t $toolchain -h ${target_hosts[$arch]} -a ${target_api[$arch]} -s $BASE_DIR/srt -i ${sysroots[$arch]} -b ${archabi[$arch]} -n $NDK -l $API_LEVEL -done diff --git a/scripts/build-android/mksrt b/scripts/build-android/mksrt index 4d45755a4..2af96a7aa 100755 --- a/scripts/build-android/mksrt +++ b/scripts/build-android/mksrt @@ -1,7 +1,25 @@ -#!/bin/bash +#!/bin/sh -source prepare_build +while getopts s:i:t:n:a: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done -./configure --use-openssl-pc=OFF --CMAKE_PREFIX_PATH=$install_dir --CMAKE_INSTALL_PREFIX=$install_dir --CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$api_lev --CMAKE_ANDROID_NDK=$NDK_HOME --CMAKE_ANDROID_ARCH_ABI=$arch_abi + +cd $SRC_DIR +./configure --use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=true \ +--CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \ +--CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +--CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +--enable-c++11 --enable-stdcxx-sync \ +--enable-debug=2 --enable-logging=0 --enable-heavy-logging=0 --enable-apps=0 make make install diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index 726e4bb05..d0305fab7 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -1,27 +1,21 @@ #!/bin/sh -while getopts n:o:a: option +while getopts n:o:a:t: option do case "${option}" in - n) ndk_home=${OPTARG};; - o) ssl_ver=${OPTARG};; - a) api_lev=${OPTARG};; + n) ANDROID_NDK=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; *) twentytwo=${OPTARG};; esac done -ANDROID_NDK=$ndk_home -OPENSSL_VERSION=$ssl_ver - -API_LEVEL=$api_lev - BUILD_DIR=/tmp/openssl_android_build OUT_DIR=$(pwd) -BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" - if [ ! -d openssl-${OPENSSL_VERSION} ] then if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ] diff --git a/scripts/build-android/packjni b/scripts/build-android/packjni deleted file mode 100755 index 80e19ac80..000000000 --- a/scripts/build-android/packjni +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -BASE_DIR=$(readlink -f $0 | xargs dirname) - -declare -A sysroots -sysroots=( - [armeabi]=$BASE_DIR/armeabi - [arm]=$BASE_DIR/armeabi-v7a - [arm64]=$BASE_DIR/arm64-v8a - [x86]=$BASE_DIR/x86 - [x86_64]=$BASE_DIR/x86_64 -) - -srt_version=1.4.2 -jni_dir=$BASE_DIR/jniLibs - -declare -A jnilibs -jnilibs=( - [armeabi=$jni_dir/armeabi - [arm]=$jni_dir/armeabi-v7a - [arm64]=$jni_dir/arm64-v8a - [x86]=$jni_dir/x86 - [x86_64]=$jni_dir/x86_64 -) - -jni_dir=$BASE_DIR/jniLibs - -# Android < 6.0 has an issue with loading versioned libraries -# The issue is because of library so-name libsrt.so.1 - -for arch in armeabi arm arm64 x86 x86_64; do - mkdir -p ${jnilibs[$arch]} - cp ${sysroots[$arch]}/lib/libsrt.so ${jnilibs[$arch]}/libsrt.so - /usr/local/bin/patchelf --set-soname libsrt.so ${jnilibs[$arch]}/libsrt.so - objdump -p ${jnilibs[$arch]}/libsrt.so | grep libsrt.so -done - -zip -r libsrt_$(date +%Y-%m-%d).zip jniLibs diff --git a/scripts/build-android/prepare_build b/scripts/build-android/prepare_build deleted file mode 100644 index c0b2cd987..000000000 --- a/scripts/build-android/prepare_build +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -while getopts t:h:a:s:i:b:n:l: option -do - case "${option}" - in - t) toolchain_dir=${OPTARG};; - h) target_host=${OPTARG};; - a) target_host_api=${OPTARG};; - s) src_dir=${OPTARG};; - i) install_dir=$OPTARG;; - b) arch_abi=${OPTARG};; - n) NDK_HOME=${OPTARG};; - l) api_lev=${OPTARG};; - *) twentytwo=${OPTARG};; - esac -done - -# Add toolchain to the search path. -export PATH=$PATH:$toolchain_dir/bin - -# Tell configure what tools to use. -export AR=$target_host-ar -export AS=$target_host-as -export LD=$target_host-ld -export STRIP=$target_host-strip - -# Tell configure which android api to use. -export CC=$target_host_api-clang -export CXX=$target_host_api-clang++ - -# Tell configure what flags Android requires. -export CFLAGS="-fPIE -fPIC" -export LDFLAGS="-pie" - -cd $src_dir From 345bab754c0a7580bde4a975a2455e36c958628d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 18 May 2021 10:21:35 +0200 Subject: [PATCH 178/790] [core] Fixed version rejection for HSv4 caller --- srtcore/core.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a1985379d..f82f7875a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2400,7 +2400,17 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, } if (hs.m_iVersion < HS_VERSION_SRT1) + { + if (m_config.uMinimumPeerSrtVersion && m_config.uMinimumPeerSrtVersion >= SRT_VERSION_FEAT_HSv5) + { + m_RejectReason = SRT_REJ_VERSION; + // This means that a version with minimum 1.3.0 that features HSv5 is required, + // hence all HSv4 clients should be rejected. + LOGP(cnlog.Error, "interpretSrtHandshake: minimum peer version 1.3.0 (HSv5 only), rejecting HSv4 client"); + return false; + } return true; // do nothing + } // Anyway, check if the handshake contains any extra data. if (hspkt.getLength() <= CHandShake::m_iContentSize) @@ -10618,16 +10628,31 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // (or the above procedure failed) if (result == -1) { - HLOGC(cnlog.Debug, - log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" - << RequestTypeStr(hs.m_iReqType)); - size_t size = CHandShake::m_iContentSize; - hs.store_to((packet.m_pcData), (size)); - packet.setLength(size); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + if (hs.m_iVersion < HS_VERSION_SRT1) + { + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: HSv4 caller, sending SHUTDOWN after rejection with " + << RequestTypeStr(hs.m_iReqType)); + // The HSv4 clients do not interpret the error handshake response correctly. + // In order to really disallow them to connect there's needed the shutdown response. + CPacket rsp; + setPacketTS((rsp), steady_clock::now()); + rsp.pack(UMSG_SHUTDOWN); + rsp.m_iID = m_PeerID; + m_pSndQueue->sendto(addr, rsp); + } + else + { + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" + << RequestTypeStr(hs.m_iReqType)); + size_t size = CHandShake::m_iContentSize; + hs.store_to((packet.m_pcData), (size)); + packet.setLength(size); + packet.m_iID = id; + setPacketTS(packet, steady_clock::now()); + HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); + m_pSndQueue->sendto(addr, packet); + } } // new connection response should be sent in acceptAndRespond() // turn the socket writable if this is the first time when this was found out. From 7e5b3ee4d834522e1b65e0e0c475c89fda58a097 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 18 May 2021 11:49:33 +0200 Subject: [PATCH 179/790] [docs] Added buffer configuration guide (#1951) --- docs/API/configuration-guidelines.md | 110 +++++++++++++++++++++++++++ docs/README.md | 15 ++-- 2 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 docs/API/configuration-guidelines.md diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md new file mode 100644 index 000000000..f0566d7d0 --- /dev/null +++ b/docs/API/configuration-guidelines.md @@ -0,0 +1,110 @@ +# Configuration Guidelines + +## Receiver Buffer Size + +The receiver buffer can be configured with the [`SRTO_RCVBUF`](./API-socket-options.md#SRTO_RCVBUF) socket option. +Buffer size in bytes is expected to be passed in the `optval` argument of the `srt_setsockopt(..)` function. +However, internally the value will be converted into the number of packets stored in the receiver buffer. + +The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`]((./API-socket-options.md#SRTO_FC) socket option. +See issue [#700](https://github.com/Haivision/srt/issues/700). + +The default flow control window size is 25600 packets. It is approximately: + +- **270 Mbits** of payload in the default live streaming configuration with an SRT payload size of **1316 bytes**; +- **300 Mbits** of payload in the default file transfer configuration with an SRT payload size of **1456 bytes**. + +The default receiver buffer size is 8192 packets. It is approximately: +- **86 Mbits** of payload with the effective SRT payload size of **1316 bytes**. + +### Setting Receiver Buffer Size + +As already mentioned, the maximum allowed size of the receiver buffer is limited by the value of `SRTO_FC`. +When the `SRTO_RCVBUF` option value is set using the `srt_setsockopt(..)` function, +the provided size in bytes is internally converted to the corresponding size in packets +using the configured value of the `SRTO_MSS` option to estimate the maximum possible payload of a packet. + +The following function returns the buffer size in packets: + +```c++ +int getRbufSizePkts(int SRTO_RCVBUF, int SRTO_MSS, int SRTO_FC) +{ + // UDP header size is assumed to be 28 bytes + // 20 bytes IPv4 + 8 bytes of UDP + const int UDPHDR_SIZE = 28; + const in pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); + + return min(pkts, SRTO_FC); +} +``` + +If the value of `SRTO_RCVBUF` in packets exceeds `SRTO_FC`, then it is silently set to the value in bytes corresponding to `SRTO_FC`. +Therefore, to set higher values of `SRTO_RCVBUF` the value of `SRTO_FC` must be increased first. + +### Calculating Target Size in Packets + +The minimum size of the receiver buffer in packets can be calculated as follows: + +`pktsRBufSize = bps / 8 Ɨ (RTTsec + latency_sec) / bytePayloadSize` + +where + +- `bps` is the payload bitrate of the stream in bits per second; +- `RTTsec` is the RTT of the network connection in seconds; + +- `bytePayloadSize` is the expected size of the payload of the SRT data packet. + +If the whole remainder of the MTU is expected to be used, payload size is calculated as follows: + +`bytePayloadSize = MSS - 44` + +where + +- 44 is the size in bytes of an **IPv4** header: + - 20 bytes **IPv4** + - 8 bytes of UDP + - 16 bytes of SRT packet header. + +- `MSS` is the Maximum Segment Size (aka MTU); see `SRTO_MSS`. + +### Calculating Target Size to Set + +To determine the value to pass in `srt_setsockopt(..)` with `SRTO_RCVBUF` +the size in packets `pktsRBufSize` must be converted to the size in bytes +assuming the internal conversion of the `srt_setsockopt(..)` function. + +The target size of the payload stored by the receiver buffer would be: + +`SRTO_RCVBUF = pktsRBufSize Ɨ (SRTO_MSS - UDPHDR_SIZE)` + +where + +- `UDPHDR_SIZE` = 28 (20 bytes IPv4, 8 bytes of UDP) +- `SRTO_MSS` is the corresponding socket option value at the moment of setting `SRTO_RCVBUF`. + + +### Summing Up + + +```c++ + +auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS, int SRTO_FC) +{ + const int UDPHDR_SIZE = 28; + const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; + const int targetNumPackets = targetPayloadBytes / bytesPayloadSize; + const int targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); + return {targetNumPackets, targetSizeValue}; +} + +// Configuring + +const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS, SRTO_FC); + +int optval = fc; +int optlen = sizeof optval; +srt_setsockopt(sock, 0, SRTO_FC, (void*) &optval, optlen); + +optval = rcvbuf; +srt_setsockopt(sock, 0, SRTO_RCVBUF, (void*) &optval, optlen); +``` diff --git a/docs/README.md b/docs/README.md index 3e5ce72c1..55a632bcc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,13 +2,14 @@ ## SRT API Documents -| Document Title | Folder | File Name | Description | -| :-------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | -| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | -| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | -| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | -| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | -| | | | | +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | +| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | +| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | +| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | +| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | +| [Configuration Guidelines](API/configuration-guidelines.md) | [API](API/) | [configuration-guidelines.md](API/configuration-guidelines.md) | How to configure SRT buffers. | +| | | | | ## Build Instructions From d9150eaa8a791b6cba712ea68290e0beabed489d Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 May 2021 23:05:50 +0800 Subject: [PATCH 180/790] [core] Fixed DROPREQ by TTL packet drop (#2003) The sequence range to drop in the sender's drop request was 1 packet wider than needed. --- srtcore/buffer.cpp | 10 +++++++++- srtcore/core.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index b29aa9322..d42a57577 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -523,6 +523,10 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // by sequence number. Consider using some circular buffer. for (int i = 0; i < offset; ++i) p = p->m_pNext; +#if ENABLE_HEAVY_LOGGING + const int32_t first_seq = p->m_iSeqNo; + int32_t last_seq = p->m_iSeqNo; +#endif // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -546,6 +550,9 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time bool move = false; while (msgno == p->getMsgSeq()) { +#if ENABLE_HEAVY_LOGGING + last_seq = p->m_iSeqNo; +#endif if (p == m_pCurrBlock) move = true; p = p->m_pNext; @@ -555,7 +562,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, " << w_msglen << " messages to drop, up to " << msgno); + 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 diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f82f7875a..a6dfb04cd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8881,10 +8881,11 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime { int32_t seqpair[2]; seqpair[0] = w_packet.m_iSeqNo; - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); + SRT_ASSERT(msglen >= 1); + seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); HLOGC(qrlog.Debug, log << "IPE: loss-reported packets not found in SndBuf - requesting DROP: " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" + << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); @@ -8892,7 +8893,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime m_pSndLossList->removeUpTo(seqpair[1]); // skip all dropped packets - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); continue; } @@ -11288,4 +11289,3 @@ void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) } #endif } - From 189b1710a08c543aaae20b6d88c76e60afbffc05 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 18 May 2021 14:26:00 +0200 Subject: [PATCH 181/790] [build] Fixed __MINGW32__ macro usage. Previously a custom __MINGW__ preprocessor definition was used. It was defined in udt.h, and not included everywhere. --- srtcore/platform_sys.h | 2 +- srtcore/srt.h | 6 +++--- srtcore/srt_compat.h | 2 +- srtcore/sync.cpp | 2 +- srtcore/udt.h | 8 -------- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index ed66e02bf..9fa2c7d32 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -30,7 +30,7 @@ #include #include -#ifndef __MINGW__ +#ifndef __MINGW32__ #include #endif diff --git a/srtcore/srt.h b/srtcore/srt.h index 92be107d2..60f3fc1e8 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -35,7 +35,7 @@ written by #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ // Explicitly define 32-bit and 64-bit numbers typedef __int32 int32_t; typedef __int64 int64_t; @@ -56,7 +56,7 @@ written by #else #define SRT_API #endif - #else // __MINGW__ + #else // __MINGW32__ #define SRT_API #endif #else @@ -152,7 +152,7 @@ typedef int32_t SRTSOCKET; static const int32_t SRTGROUP_MASK = (1 << 30); #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ typedef SOCKET SYSSOCKET; #else typedef int SYSSOCKET; diff --git a/srtcore/srt_compat.h b/srtcore/srt_compat.h index f7d072393..960c1b85a 100644 --- a/srtcore/srt_compat.h +++ b/srtcore/srt_compat.h @@ -22,7 +22,7 @@ written by #ifndef SRT_API #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ #ifdef SRT_DYNAMIC #ifdef SRT_EXPORTS #define SRT_API __declspec(dllexport) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 7fb494256..c630f897d 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -227,7 +227,7 @@ bool srt::sync::CTimer::sleep_until(TimePoint tp) __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); #elif AMD64 __asm__ volatile ("nop; nop; nop; nop; nop;"); -#elif defined(_WIN32) && !defined(__MINGW__) +#elif defined(_WIN32) && !defined(__MINGW32__) __nop(); __nop(); __nop(); diff --git a/srtcore/udt.h b/srtcore/udt.h index 1bcc889ca..8d7d5eb87 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -82,13 +82,6 @@ modified by #define INCREMENT_THREAD_ITERATIONS() #endif -/* Obsolete way to define MINGW */ -#ifndef __MINGW__ -#if defined(__MINGW32__) || defined(__MINGW64__) -#define __MINGW__ 1 -#endif -#endif - #ifdef __cplusplus #include #include @@ -96,7 +89,6 @@ modified by #include #endif - //////////////////////////////////////////////////////////////////////////////// //if compiling on VC6.0 or pre-WindowsXP systems From 393a6c7041f6d883342a0bb6ca7c6d840f471229 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 12 May 2021 13:37:11 +0200 Subject: [PATCH 182/790] [core] Added srt::sync::genRandomInt(..) for a uniform way to get a random integer from the range. If C++11 is enabled, uniform_int_distribution and random_device are used. --- srtcore/api.cpp | 17 +--------- srtcore/channel.cpp | 8 +---- srtcore/congctl.cpp | 8 ++--- srtcore/core.h | 4 +-- srtcore/sync.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/sync.h | 12 +++++++ srtcore/utilities.h | 1 - test/test_sync.cpp | 21 ++++++++++++ 8 files changed, 120 insertions(+), 32 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5bd307125..542832f77 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -194,22 +194,7 @@ m_bGCStatus(false), m_ClosedSockets() { // Socket ID MUST start from a random value - // Note. Don't use CTimer here, because s_UDTUnited is a static instance of CUDTUnited - // with dynamic initialization (calling this constructor), while CTimer has - // a static member s_ullCPUFrequency with dynamic initialization. - // The order of initialization is not guaranteed. - timeval t; - - gettimeofday(&t, 0); - srand((unsigned int)t.tv_usec); - - const double rand1_0 = double(rand())/RAND_MAX; - - // Motivation: in case when rand() returns the value equal to RAND_MAX, - // rand1_0 == 1, so the below formula will be - // 1 + (MAX_SOCKET_VAL-1) * 1 = 1 + MAX_SOCKET_VAL - 1 = MAX_SOCKET_VAL - // which is the highest allowed value for the socket. - m_SocketIDGenerator = 1 + int((MAX_SOCKET_VAL-1) * rand1_0); + m_SocketIDGenerator = genRandomInt(1, MAX_SOCKET_VAL); m_SocketIDGenerator_init = m_SocketIDGenerator; // XXX An unlikely exception thrown from the below calls diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 865eaaabc..f78689a3a 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -562,12 +562,6 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (!packet.isControl()) { - if (dcounter == 0) - { - timeval tv; - gettimeofday(&tv, 0); - srand(tv.tv_usec & 0xFFFF); - } ++dcounter; if (flwcounter) @@ -581,7 +575,7 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > 8) { // Make a random number in the range between 8 and 24 - int rnd = rand() % 16 + SRT_TEST_FAKE_LOSS; + const int rnd = srt::sync::getRandomInt(8, 24); if (dcounter > rnd) { diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 0b00929b7..fdf9ddb0b 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -525,11 +525,9 @@ class FileCC : public SrtCongestionControlBase m_iLastDecSeq = m_parent->sndSeqNo(); - // remove global synchronization using randomization - srand(m_iLastDecSeq); - m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); - if (m_iDecRandom < 1) - m_iDecRandom = 1; + // remove global synchronization using randomization. + m_iDecRandom = genRandomInt(1, m_iAvgNAKNum); + SRT_ASSERT(m_iDecRandom >= 1); HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin << ", lastsentseqno=" << m_iLastDecSeq << ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin) diff --git a/srtcore/core.h b/srtcore/core.h index 97eda7436..1d75bbaea 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -401,9 +401,7 @@ class CUDT static int32_t generateISN() { using namespace srt::sync; - // Random Initial Sequence Number (normal mode) - srand((unsigned) count_microseconds(steady_clock::now().time_since_epoch())); - return (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } // For SRT_tsbpdLoop diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index c630f897d..820e0a7f9 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -18,11 +18,18 @@ #include "logging.h" #include "common.h" +// HAVE_CXX11 is defined in utilities.h, included with common.h. +// The following conditional inclusion must go after common.h. +#if HAVE_CXX11 +#include +#endif + namespace srt_logging { extern Logger inlog; } using namespace srt_logging; +using namespace std; namespace srt { @@ -267,3 +274,77 @@ bool srt::sync::CGlobEvent::waitForEvent() return g_Sync.lock_wait_for(milliseconds_from(10)); } +//////////////////////////////////////////////////////////////////////////////// +// +// Random +// +//////////////////////////////////////////////////////////////////////////////// + +namespace srt +{ +#if HAVE_CXX11 +static std::random_device& randomDevice() +{ + static std::random_device s_RandomDevice; + return s_RandomDevice; +} +#elif defined(_WIN32) && defined(__MINGW32__) +static void initRandSeed() +{ + const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); + srand((unsigned int) seed); +} +static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT; +#else + +static unsigned int genRandSeed() +{ + // Duration::count() does not depend on any global objects, + // therefore it is preferred over count)microseconds(..). + const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); + return (unsigned int) seed; +} + +static unsigned int* getRandSeed() +{ + static unsigned int s_uRandSeed = genRandSeed(); + return &s_uRandSeed; +} + +#endif +} + +int srt::sync::genRandomInt(int minVal, int maxVal) +{ + // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03. + // A mutex to protect simulteneout access to the random device. + // Thread-local storage could be used here instead to store the seed / random device. + // However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC), + // so sharing a single seed among threads should not impact the performance. + static sync::Mutex s_mtxRandomDevice; + sync::ScopedLock lck(s_mtxRandomDevice); +#if HAVE_CXX11 + uniform_int_distribution<> dis(minVal, maxVal); + return dis(randomDevice()); +#else +#if defined(__MINGW32__) + // No rand_r(..) for MinGW. + pthread_once(&s_InitRandSeedOnce, initRandSeed); + // rand() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive + // (i.e., the mathematical range [0, RAND_MAX]). + // Therefore, rand_0_1 belongs to [0.0, 1.0]. + const double rand_0_1 = double(rand()) / RAND_MAX; +#else // not __MINGW32__ + // rand_r(..) returns a pseudo-random integer in the range 0 to RAND_MAX inclusive + // (i.e., the mathematical range [0, RAND_MAX]). + // Therefore, rand_0_1 belongs to [0.0, 1.0]. + const double rand_0_1 = double(rand_r(getRandSeed())) / RAND_MAX; +#endif + + // Map onto [minVal, maxVal]. + // Note. The probablity to get maxVal as the result is minuscule. + const int res = minVal + static_cast((maxVal - minVal) * rand_0_1); + return res; +#endif // HAVE_CXX11 +} + diff --git a/srtcore/sync.h b/srtcore/sync.h index 7457c3df9..f25c5ca8d 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -835,6 +835,18 @@ void SetThreadLocalError(const CUDTException& e); /// @returns CUDTException pointer CUDTException& GetThreadLocalError(); +//////////////////////////////////////////////////////////////////////////////// +// +// Random distribution functions. +// +//////////////////////////////////////////////////////////////////////////////// + +/// Generate a uniform-distributed random integer from [minVal; maxVal]. +/// If HAVE_CXX11, uses std::uniform_distribution(std::random_device). +/// @param[in] minVal minimum allowed value of the resulting random number. +/// @param[in] maxVal maximum allowed value of the resulting random number. +int genRandomInt(int minVal, int maxVal); + } // namespace sync } // namespace srt diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 7d73c8567..caf882cd9 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -34,7 +34,6 @@ written by #define ATR_UNUSED #define ATR_DEPRECATED #endif - #if defined(__cplusplus) && __cplusplus > 199711L #define HAVE_CXX11 1 diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 47d23f7bc..950af154a 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -157,6 +157,27 @@ TEST(SyncDuration, OperatorMultIntEq) EXPECT_EQ(count_milliseconds(a), 7000); } +TEST(SyncRandom, GenRandomInt) +{ + vector mn(64); + + for (int i = 0; i < 2048; ++i) + { + const int rand_val = genRandomInt(0, 63); + ASSERT_GE(rand_val, 0); + ASSERT_LE(rand_val, 63); + ++mn[rand_val]; + } + + // Uncomment to see the distribution. + // for (size_t i = 0; i < mn.size(); ++i) + // { + // cout << i << '\t'; + // for (int j=0; j Date: Wed, 19 May 2021 12:23:31 +0200 Subject: [PATCH 183/790] [core] Fixed DROPREQ on NAK behind SndLastDataAck (#2011) --- srtcore/buffer.cpp | 5 +++-- srtcore/core.cpp | 8 +++++--- srtcore/srt.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index d42a57577..bce9922b6 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -183,7 +183,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) int32_t& w_msgno = w_mctrl.msgno; int32_t& w_seqno = w_mctrl.pktseq; int64_t& w_srctime = w_mctrl.srctime; - int& w_ttl = w_mctrl.msgttl; + const int& ttl = w_mctrl.msgttl; int size = len / m_iMSS; if ((len % m_iMSS) != 0) size++; @@ -254,7 +254,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) s->m_llSourceTime_us = w_srctime; s->m_tsOriginTime = time; s->m_tsRexmitTime = time_point(); - s->m_iTTL = w_ttl; + s->m_iTTL = ttl; // Rewrite the actual sending time back into w_srctime // so that the calling facilities can reuse it if (!w_srctime) @@ -542,6 +542,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // if found block is stale // (This is for messages that have declared TTL - messages that fail to be sent // before the TTL defined time comes, will be dropped). + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { int32_t msgno = p->getMsgSeq(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a6dfb04cd..f6c6cac0a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8848,9 +8848,11 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime // 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]; - seqpair[0] = w_packet.m_iSeqNo; - seqpair[1] = m_iSndLastDataAck; + int32_t seqpair[2] = { + w_packet.m_iSeqNo, + CSeqNo::decseq(m_iSndLastDataAck) + }; + w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, log << "PEER reported LOSS not from the sending buffer - requesting DROP: " << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" diff --git a/srtcore/srt.h b/srtcore/srt.h index 60f3fc1e8..21e94879e 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -863,7 +863,7 @@ SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* op typedef struct SRT_MsgCtrl_ { int flags; // Left for future - int msgttl; // TTL for a message, default -1 (no TTL limitation) + int msgttl; // TTL for a message (millisec), default -1 (no TTL limitation) int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode. int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame int64_t srctime; // source time since epoch (usec), 0: use internal time (sender) From 4ddb68efaed2fa847ed02eac61a81e68bff7b94d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 10:45:24 +0200 Subject: [PATCH 184/790] [core] Applied clang-format on queue.h and cpp --- srtcore/queue.cpp | 328 +++++++++++++------------ srtcore/queue.h | 605 +++++++++++++++++++++++----------------------- 2 files changed, 482 insertions(+), 451 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index c2aa8d40d..ccc14b9b8 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -78,14 +78,14 @@ CUnitQueue::CUnitQueue() CUnitQueue::~CUnitQueue() { - CQEntry *p = m_pQEntry; + CQEntry* p = m_pQEntry; while (p != NULL) { delete[] p->m_pUnit; delete[] p->m_pBuffer; - CQEntry *q = p; + CQEntry* q = p; if (p == m_pLastQueue) p = NULL; else @@ -96,9 +96,9 @@ CUnitQueue::~CUnitQueue() int CUnitQueue::init(int size, int mss, int version) { - CQEntry *tempq = NULL; - CUnit * tempu = NULL; - char * tempb = NULL; + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; try { @@ -142,11 +142,11 @@ int CUnitQueue::increase() { // adjust/correct m_iCount int real_count = 0; - CQEntry *p = m_pQEntry; + CQEntry* p = m_pQEntry; while (p != NULL) { - CUnit *u = p->m_pUnit; - for (CUnit *end = u + p->m_iSize; u != end; ++u) + CUnit* u = p->m_pUnit; + for (CUnit* end = u + p->m_iSize; u != end; ++u) if (u->m_iFlag != CUnit::FREE) ++real_count; @@ -159,9 +159,9 @@ int CUnitQueue::increase() if (double(m_iCount) / m_iSize < 0.9) return -1; - CQEntry *tempq = NULL; - CUnit * tempu = NULL; - char * tempb = NULL; + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; // all queues have the same size const int size = m_pQEntry->m_iSize; @@ -179,8 +179,8 @@ int CUnitQueue::increase() delete[] tempb; LOGC(rslog.Error, - log << "CUnitQueue:increase: failed to allocate " << size << " new units." - << " Current size=" << m_iSize); + log << "CUnitQueue:increase: failed to allocate " << size << " new units." + << " Current size=" << m_iSize); return -1; } @@ -208,7 +208,7 @@ int CUnitQueue::shrink() return -1; } -CUnit *CUnitQueue::getNextAvailUnit() +CUnit* CUnitQueue::getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); @@ -219,7 +219,7 @@ CUnit *CUnitQueue::getNextAvailUnit() int units_checked = 0; do { - const CUnit *end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize; + const CUnit* end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize; for (; m_pAvailUnit != end; ++m_pAvailUnit, ++units_checked) { if (m_pAvailUnit->m_iFlag == CUnit::FREE) @@ -237,7 +237,7 @@ CUnit *CUnitQueue::getNextAvailUnit() return NULL; } -void CUnitQueue::makeUnitFree(CUnit *unit) +void CUnitQueue::makeUnitFree(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); @@ -245,7 +245,7 @@ void CUnitQueue::makeUnitFree(CUnit *unit) --m_iCount; } -void CUnitQueue::makeUnitGood(CUnit *unit) +void CUnitQueue::makeUnitGood(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); @@ -262,7 +262,7 @@ CSndUList::CSndUList() , m_pWindowCond(NULL) , m_pTimer(NULL) { - m_pHeap = new CSNode *[m_iArrayLength]; + m_pHeap = new CSNode*[m_iArrayLength]; } CSndUList::~CSndUList() @@ -307,7 +307,7 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) return -1; - CUDT *u = m_pHeap[0]->m_pUDT; + CUDT* u = m_pHeap[0]->m_pUDT; remove_(u); #define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " @@ -337,7 +337,7 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) return 1; } -void CSndUList::remove(const CUDT *u) +void CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); @@ -356,18 +356,18 @@ steady_clock::time_point CSndUList::getNextProcTime() void CSndUList::realloc_() { - CSNode **temp = NULL; + CSNode** temp = NULL; try { - temp = new CSNode *[2 * m_iArrayLength]; + temp = new CSNode*[2 * m_iArrayLength]; } catch (...) { throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); } - memcpy((temp), m_pHeap, sizeof(CSNode *) * m_iArrayLength); + memcpy((temp), m_pHeap, sizeof(CSNode*) * m_iArrayLength); m_iArrayLength *= 2; delete[] m_pHeap; m_pHeap = temp; @@ -384,7 +384,7 @@ void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) { - CSNode *n = u->m_pSNode; + CSNode* n = u->m_pSNode; // do not insert repeated node if (n->m_iHeapLoc >= 0) @@ -394,7 +394,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT m_iLastEntry++; m_pHeap[m_iLastEntry] = n; - n->m_tsTimeStamp = ts; + n->m_tsTimeStamp = ts; int q = m_iLastEntry; int p = q; @@ -424,7 +424,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT void CSndUList::remove_(const CUDT* u) { - CSNode *n = u->m_pSNode; + CSNode* n = u->m_pSNode; if (n->m_iHeapLoc >= 0) { @@ -493,14 +493,20 @@ CSndQueue::~CSndQueue() delete m_pSndUList; } -int CSndQueue::ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } -int CSndQueue::sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } +int CSndQueue::ioctlQuery(int type) const +{ + return m_pChannel->ioctlQuery(type); +} +int CSndQueue::sockoptQuery(int level, int type) const +{ + return m_pChannel->sockoptQuery(level, type); +} #if ENABLE_LOGGING - int CSndQueue::m_counter = 0; +int CSndQueue::m_counter = 0; #endif -void CSndQueue::init(CChannel *c, CTimer *t) +void CSndQueue::init(CChannel* c, CTimer* t) { m_pChannel = c; m_pTimer = t; @@ -512,7 +518,7 @@ void CSndQueue::init(CChannel *c, CTimer *t) #if ENABLE_LOGGING ++m_counter; const std::string thrname = "SRT:SndQ:w" + Sprint(m_counter); - const char* thname = thrname.c_str(); + const char* thname = thrname.c_str(); #else const char* thname = "SRT:SndQ"; #endif @@ -520,17 +526,26 @@ void CSndQueue::init(CChannel *c, CTimer *t) throw CUDTException(MJ_SYSTEMRES, MN_THREAD); } -int CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : -1; } +int CSndQueue::getIpTTL() const +{ + return m_pChannel ? m_pChannel->getIpTTL() : -1; +} -int CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } +int CSndQueue::getIpToS() const +{ + return m_pChannel ? m_pChannel->getIpToS() : -1; +} #ifdef SRT_ENABLE_BINDTODEVICE -bool CSndQueue::getBind(char* dst, size_t len) const { return m_pChannel ? m_pChannel->getBind(dst, len) : false; } +bool CSndQueue::getBind(char* dst, size_t len) const +{ + return m_pChannel ? m_pChannel->getBind(dst, len) : false; +} #endif -void *CSndQueue::worker(void *param) +void* CSndQueue::worker(void* param) { - CSndQueue *self = (CSndQueue *)param; + CSndQueue* self = (CSndQueue*)param; #if ENABLE_LOGGING THREAD_STATE_INIT(("SRT:SndQ:w" + Sprint(m_counter)).c_str()); @@ -558,8 +573,8 @@ void *CSndQueue::worker(void *param) self->m_WorkerStats.lNotReadyTs++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ - UniqueLock windlock (self->m_WindowLock); - CSync windsync (self->m_WindowCond, windlock); + UniqueLock windlock(self->m_WindowLock); + CSync windsync(self->m_WindowCond, windlock); // wait here if there is no sockets with data to be sent THREAD_PAUSED(); @@ -646,9 +661,9 @@ CRcvUList::CRcvUList() CRcvUList::~CRcvUList() {} -void CRcvUList::insert(const CUDT *u) +void CRcvUList::insert(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; n->m_tsTimeStamp = steady_clock::now(); if (NULL == m_pUList) @@ -667,9 +682,9 @@ void CRcvUList::insert(const CUDT *u) m_pLast = n; } -void CRcvUList::remove(const CUDT *u) +void CRcvUList::remove(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; @@ -698,9 +713,9 @@ void CRcvUList::remove(const CUDT *u) n->m_pNext = n->m_pPrev = NULL; } -void CRcvUList::update(const CUDT *u) +void CRcvUList::update(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; @@ -739,10 +754,10 @@ CHash::~CHash() { for (int i = 0; i < m_iHashSize; ++i) { - CBucket *b = m_pBucket[i]; + CBucket* b = m_pBucket[i]; while (NULL != b) { - CBucket *n = b->m_pNext; + CBucket* n = b->m_pNext; delete b; b = n; } @@ -753,7 +768,7 @@ CHash::~CHash() void CHash::init(int size) { - m_pBucket = new CBucket *[size]; + m_pBucket = new CBucket*[size]; for (int i = 0; i < size; ++i) m_pBucket[i] = NULL; @@ -761,10 +776,10 @@ void CHash::init(int size) m_iHashSize = size; } -CUDT *CHash::lookup(int32_t id) +CUDT* CHash::lookup(int32_t id) { // simple hash function (% hash table size); suitable for socket descriptors - CBucket *b = m_pBucket[id % m_iHashSize]; + CBucket* b = m_pBucket[id % m_iHashSize]; while (NULL != b) { @@ -776,11 +791,11 @@ CUDT *CHash::lookup(int32_t id) return NULL; } -void CHash::insert(int32_t id, CUDT *u) +void CHash::insert(int32_t id, CUDT* u) { - CBucket *b = m_pBucket[id % m_iHashSize]; + CBucket* b = m_pBucket[id % m_iHashSize]; - CBucket *n = new CBucket; + CBucket* n = new CBucket; n->m_iID = id; n->m_pUDT = u; n->m_pNext = b; @@ -790,8 +805,8 @@ void CHash::insert(int32_t id, CUDT *u) void CHash::remove(int32_t id) { - CBucket *b = m_pBucket[id % m_iHashSize]; - CBucket *p = NULL; + CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* p = NULL; while (NULL != b) { @@ -824,26 +839,28 @@ CRendezvousQueue::~CRendezvousQueue() m_lRendezvousID.clear(); } -void CRendezvousQueue::insert( - const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) +void CRendezvousQueue::insert(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const steady_clock::time_point& ttl) { ScopedLock vg(m_RIDListLock); CRL r; - r.m_iID = id; - r.m_pUDT = u; + r.m_iID = id; + r.m_pUDT = u; r.m_PeerAddr = addr; - r.m_tsTTL = ttl; + r.m_tsTTL = ttl; m_lRendezvousID.push_back(r); - HLOGC(cnlog.Debug, log << "RID: adding socket @" << id << " for address: " << addr.str() - << " expires: " << FormatTime(ttl) - << " (total connectors: " << m_lRendezvousID.size() << ")"); + HLOGC(cnlog.Debug, + log << "RID: adding socket @" << id << " for address: " << addr.str() << " expires: " << FormatTime(ttl) + << " (total connectors: " << m_lRendezvousID.size() << ")"); } -void CRendezvousQueue::remove(const SRTSOCKET &id) +void CRendezvousQueue::remove(const SRTSOCKET& id) { - ScopedLock lkv (m_RIDListLock); + ScopedLock lkv(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { @@ -857,35 +874,36 @@ void CRendezvousQueue::remove(const SRTSOCKET &id) CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { - ScopedLock vg(m_RIDListLock); + ScopedLock vg(m_RIDListLock); // TODO: optimize search for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) { - HLOGC(cnlog.Debug, log << "RID: found id @" << i->m_iID << " while looking for " - << (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") - << i->m_PeerAddr.str()); + HLOGC(cnlog.Debug, + log << "RID: found id @" << i->m_iID << " while looking for " + << (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") << i->m_PeerAddr.str()); w_id = i->m_iID; return i->m_pUDT; } } #if ENABLE_HEAVY_LOGGING - std::ostringstream spec; - if (w_id == 0) - spec << "A NEW CONNECTION REQUEST"; - else - spec << " AGENT @" << w_id; - HLOGC(cnlog.Debug, log << "RID: NO CONNECTOR FOR ADR:" << addr.str() - << " while looking for " << spec.str() << " (" << m_lRendezvousID.size() << " connectors total)"); + std::ostringstream spec; + if (w_id == 0) + spec << "A NEW CONNECTION REQUEST"; + else + spec << " AGENT @" << w_id; + HLOGC(cnlog.Debug, + log << "RID: NO CONNECTOR FOR ADR:" << addr.str() << " while looking for " << spec.str() << " (" + << m_lRendezvousID.size() << " connectors total)"); #endif return NULL; } -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &pktIn) +void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) { vector toRemove, toProcess; @@ -896,8 +914,9 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " - << toRemove.size() << " to close"); + HLOGC(cnlog.Debug, + log << "updateConnStatus: collected " << toProcess.size() << " for processing, " << toRemove.size() + << " to close"); // Repeat (resend) connection request. for (vector::iterator i = toProcess.begin(); i != toProcess.end(); ++i) @@ -925,18 +944,18 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con conn_st = CONN_AGAIN; } - HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); + HLOGC(cnlog.Debug, + log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. LinkStatusInfo fi = *i; - fi.errorcode = SRT_ECONNREJ; + fi.errorcode = SRT_ECONNREJ; toRemove.push_back(fi); i->u->sendCtrl(UMSG_SHUTDOWN); } - } // NOTE: it is "believed" here that all CUDT objects will not be @@ -962,7 +981,8 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // be normally closed by the application, after it is done with them. // app can call any UDT API to learn the connection_broken error - CUDT::s_UDTUnited.m_EPoll.update_events(i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + CUDT::s_UDTUnited.m_EPoll.update_events( + i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); i->u->completeBrokenConnectionDependencies(i->errorcode); } @@ -975,15 +995,21 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { if (find_if(toRemove.begin(), toRemove.end(), LinkStatusInfo::HasID(i->m_iID)) != toRemove.end()) { - LOGC(cnlog.Error, log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID << ". Setting TTL as EXPIRED."); - i->m_tsTTL = steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration + LOGC(cnlog.Error, + log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID + << ". Setting TTL as EXPIRED."); + i->m_tsTTL = + steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration } } } } -bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, - int iDstSockID, vector& toRemove, vector& toProcess) +bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, + EConnectStatus cst SRT_ATR_UNUSED, + int iDstSockID, + vector& toRemove, + vector& toProcess) { ScopedLock vg(m_RIDListLock); @@ -991,8 +1017,8 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A return false; // nothing to process. HLOGC(cnlog.Debug, - log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID - << " status: " << ConnectStatusStr(cst)); + log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID + << " status: " << ConnectStatusStr(cst)); for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) { @@ -1003,11 +1029,11 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A if (tsNow >= i->m_tsTTL) { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID - << " removed - EXPIRED (" - // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. - << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") - << "). WILL REMOVE from queue."); + HLOGC(cnlog.Debug, + log << "RID: socket @" << i->m_iID + << " removed - EXPIRED (" + // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. + << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") << "). WILL REMOVE from queue."); // Set appropriate error information, but do not update yet. // Exit the lock first. Collect objects to update them later. @@ -1018,7 +1044,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A { // Timer expired, set TIMEOUT forcefully i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; - ccerror = SRT_ENOSERVER; + ccerror = SRT_ENOSERVER; } else { @@ -1031,7 +1057,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A // The call to completeBrokenConnectionDependencies() cannot happen here // under the lock of m_RIDListLock as it risks a deadlock. // Collect in 'toRemove' to update later. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1 }; + LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; toRemove.push_back(fi); // i_next was preincremented, but this is guaranteed to point to @@ -1041,12 +1067,14 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A } else { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << std::fixed << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + HLOGC(cnlog.Debug, + log << "RID: socket @" << i->m_iID << " still active (remaining " << std::fixed + << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; - const steady_clock::time_point tsRepeat = tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). + const steady_clock::time_point tsRepeat = + tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). // A connection request is repeated every 250 ms if there was no response from the peer: // - RST_AGAIN means no packet was received over UDP. @@ -1054,20 +1082,21 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A 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 << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 + << " ms passed since last connection request."); continue; } - HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); + HLOGC(cnlog.Debug, + log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); // This queue is used only in case of Async mode (rendezvous or caller-listener). // Synchronous connection requests are handled in startConnect() completely. if (!i->m_pUDT->m_config.bSynRecving) { // Collect them so that they can be updated out of m_RIDListLock. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1 }; + LinkStatusInfo fi = {i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; toProcess.push_back(fi); } else @@ -1116,11 +1145,11 @@ CRcvQueue::~CRcvQueue() delete m_pRendezvousQueue; // remove all queued messages - for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) { while (!i->second.empty()) { - CPacket *pkt = i->second.front(); + CPacket* pkt = i->second.front(); delete[] pkt->m_pcData; delete pkt; i->second.pop(); @@ -1129,15 +1158,14 @@ CRcvQueue::~CRcvQueue() } #if ENABLE_LOGGING - int CRcvQueue::m_counter = 0; +int CRcvQueue::m_counter = 0; #endif - -void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel *cc, CTimer *t) +void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) { m_szPayloadSize = payload; - m_UnitQueue.init(qsize, (int) payload, version); + m_UnitQueue.init(qsize, (int)payload, version); m_pHash = new CHash; m_pHash->init(hsize); @@ -1161,9 +1189,9 @@ void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel } } -void *CRcvQueue::worker(void *param) +void* CRcvQueue::worker(void* param) { - CRcvQueue * self = (CRcvQueue *)param; + CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->m_UnitQueue.getIPversion()); int32_t id = 0; @@ -1173,7 +1201,7 @@ void *CRcvQueue::worker(void *param) THREAD_STATE_INIT("SRT:RcvQ:worker"); #endif - CUnit * unit = 0; + CUnit* unit = 0; EConnectStatus cst = CONN_AGAIN; while (!self->m_bClosing) { @@ -1243,12 +1271,13 @@ void *CRcvQueue::worker(void *param) // OTHERWISE: this is an "AGAIN" situation. No data was read, but the process should continue. // take care of the timing event for all UDT sockets - const steady_clock::time_point curtime_minus_syn = steady_clock::now() - microseconds_from(CUDT::COMM_SYN_INTERVAL_US); + const steady_clock::time_point curtime_minus_syn = + steady_clock::now() - microseconds_from(CUDT::COMM_SYN_INTERVAL_US); - CRNode *ul = self->m_pRcvUList->m_pUList; + CRNode* ul = self->m_pRcvUList->m_pUList; while ((NULL != ul) && (ul->m_tsTimeStamp < curtime_minus_syn)) { - CUDT *u = ul->m_pUDT; + CUDT* u = ul->m_pUDT; if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) { @@ -1304,7 +1333,7 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad // check waiting list, if new socket, insert it to the list while (ifNewEntry()) { - CUDT *ne = getNewEntry(); + CUDT* ne = getNewEntry(); if (ne) { HLOGC(qrlog.Debug, @@ -1344,9 +1373,9 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad if (rst == RST_OK) { w_id = w_unit->m_Packet.m_iID; - HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() - << " BOUND=" << m_pChannel->bindAddressAny().str() - << " " << w_unit->m_Packet.Info()); + HLOGC(qrlog.Debug, + log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " + << w_unit->m_Packet.Info()); } return rst; } @@ -1354,20 +1383,18 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) { HLOGC(cnlog.Debug, - log << "Got sockID=0 from " << addr.str() - << " - trying to resolve it as a connection request..."); + log << "Got sockID=0 from " << addr.str() << " - trying to resolve it as a connection request..."); // Introduced protection because it may potentially happen // that another thread could have closed the socket at // the same time and inject a bug between checking the // pointer for NULL and using it. - int listener_ret = SRT_REJ_UNKNOWN; - bool have_listener = false; + int listener_ret = SRT_REJ_UNKNOWN; + bool have_listener = false; { ScopedLock cg(m_LSLock); if (m_pListener) { - LOGC(cnlog.Note, - log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); + LOGC(cnlog.Note, log << "PASSING request from: " << addr.str() << " to agent:" << 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 @@ -1398,7 +1425,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { - CUDT *u = m_pHash->lookup(id); + CUDT* u = m_pHash->lookup(id); if (!u) { // Pass this to either async rendezvous connection, @@ -1412,8 +1439,8 @@ EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, if (addr != u->m_PeerAddr) { HLOGC(cnlog.Debug, - log << CONID() << "Packet for SID=" << id << " asoc with " << u->m_PeerAddr.str() - << " received from " << addr.str() << " (CONSIDERED ATTACK ATTEMPT)"); + log << CONID() << "Packet for SID=" << id << " asoc with " << u->m_PeerAddr.str() << " received from " + << addr.str() << " (CONSIDERED ATTACK ATTEMPT)"); // This came not from the address that is the peer associated // with the socket. Ignore it. return CONN_AGAIN; @@ -1454,7 +1481,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // stored in the rendezvous queue (see CRcvQueue::registerConnector) // or simply 0, but then at least the address must match one of these. // If the id was 0, it will be set to the actual socket ID of the returned CUDT. - CUDT *u = m_pRendezvousQueue->retrieve(addr, (id)); + CUDT* u = m_pRendezvousQueue->retrieve(addr, (id)); if (!u) { // this socket is then completely unknown to the system. @@ -1525,7 +1552,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // that we KNOW (by the cst == CONN_ACCEPT result) that the socket should be inserted // into the pending anteroom. - CUDT *ne = getNewEntry(); // This function actuall removes the entry and returns it. + CUDT* ne = getNewEntry(); // This function actuall removes the entry and returns it. // This **should** now always return a non-null value, but check it first // because if this accidentally isn't true, the call to worker_ProcessAddressedPacket will // result in redirecting it to here and so on until the call stack overflow. In case of @@ -1597,10 +1624,10 @@ void CRcvQueue::stopWorker() int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) { - UniqueLock bufferlock (m_BufferLock); - CSync buffercond (m_BufferCond, bufferlock); + UniqueLock bufferlock(m_BufferLock); + CSync buffercond(m_BufferCond, bufferlock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1617,7 +1644,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) } // retrieve the earliest packet - CPacket *newpkt = i->second.front(); + CPacket* newpkt = i->second.front(); if (w_packet.getLength() < newpkt->getLength()) { @@ -1649,7 +1676,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) return (int)w_packet.getLength(); } -int CRcvQueue::setListener(CUDT *u) +int CRcvQueue::setListener(CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1660,7 +1687,7 @@ int CRcvQueue::setListener(CUDT *u) return 0; } -void CRcvQueue::removeListener(const CUDT *u) +void CRcvQueue::removeListener(const CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1668,21 +1695,24 @@ void CRcvQueue::removeListener(const CUDT *u) m_pListener = NULL; } -void CRcvQueue::registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) +void CRcvQueue::registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const steady_clock::time_point& ttl) { HLOGC(cnlog.Debug, log << "registerConnector: adding @" << id << " addr=" << addr.str() << " TTL=" << FormatTime(ttl)); m_pRendezvousQueue->insert(id, u, addr, ttl); } -void CRcvQueue::removeConnector(const SRTSOCKET &id) +void CRcvQueue::removeConnector(const SRTSOCKET& id) { HLOGC(cnlog.Debug, log << "removeConnector: removing @" << id); m_pRendezvousQueue->remove(id); ScopedLock bufferlock(m_BufferLock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { HLOGC(cnlog.Debug, @@ -1697,34 +1727,37 @@ void CRcvQueue::removeConnector(const SRTSOCKET &id) } } -void CRcvQueue::setNewEntry(CUDT *u) +void CRcvQueue::setNewEntry(CUDT* u) { HLOGC(cnlog.Debug, log << CUDTUnited::CONID(u->m_SocketID) << "setting socket PENDING FOR CONNECTION"); ScopedLock listguard(m_IDLock); m_vNewEntry.push_back(u); } -bool CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } +bool CRcvQueue::ifNewEntry() +{ + return !(m_vNewEntry.empty()); +} -CUDT *CRcvQueue::getNewEntry() +CUDT* CRcvQueue::getNewEntry() { ScopedLock listguard(m_IDLock); if (m_vNewEntry.empty()) return NULL; - CUDT *u = (CUDT *)*(m_vNewEntry.begin()); + CUDT* u = (CUDT*)*(m_vNewEntry.begin()); m_vNewEntry.erase(m_vNewEntry.begin()); return u; } -void CRcvQueue::storePkt(int32_t id, CPacket *pkt) +void CRcvQueue::storePkt(int32_t id, CPacket* pkt) { - UniqueLock bufferlock (m_BufferLock); - CSync passcond (m_BufferCond, bufferlock); + UniqueLock bufferlock(m_BufferLock); + CSync passcond(m_BufferCond, bufferlock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1741,10 +1774,9 @@ void CRcvQueue::storePkt(int32_t id, CPacket *pkt) } } - void CMultiplexer::destroy() { - // Reverse order of the assigned + // Reverse order of the assigned delete m_pRcvQueue; delete m_pSndQueue; delete m_pTimer; diff --git a/srtcore/queue.h b/srtcore/queue.h index 9c0affd5e..a412fd516 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -50,7 +50,6 @@ modified by Haivision Systems Inc. *****************************************************************************/ - #ifndef INC_SRT_QUEUE_H #define INC_SRT_QUEUE_H @@ -69,255 +68,257 @@ class CChannel; struct CUnit { - CPacket m_Packet; // packet - enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 }; - Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped + CPacket m_Packet; // packet + enum Flag + { + FREE = 0, + GOOD = 1, + PASSACK = 2, + DROPPED = 3 + }; + Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped }; class CUnitQueue { public: + CUnitQueue(); + ~CUnitQueue(); - CUnitQueue(); - ~CUnitQueue(); - -public: // Storage size operations - - /// Initialize the unit queue. - /// @param [in] size queue size - /// @param [in] mss maximum segment size - /// @param [in] version IP version - /// @return 0: success, -1: failure. +public: // Storage size operations + /// Initialize the unit queue. + /// @param [in] size queue size + /// @param [in] mss maximum segment size + /// @param [in] version IP version + /// @return 0: success, -1: failure. - int init(int size, int mss, int version); + int init(int size, int mss, int version); - /// Increase (double) the unit queue size. - /// @return 0: success, -1: failure. + /// Increase (double) the unit queue size. + /// @return 0: success, -1: failure. - int increase(); + int increase(); - /// Decrease (halve) the unit queue size. - /// @return 0: success, -1: failure. + /// Decrease (halve) the unit queue size. + /// @return 0: success, -1: failure. - int shrink(); + int shrink(); public: - int size() const { return m_iSize - m_iCount; } - int capacity() const { return m_iSize; } + int size() const { return m_iSize - m_iCount; } + int capacity() const { return m_iSize; } -public: // Operations on units +public: // Operations on units + /// find an available unit for incoming packet. + /// @return Pointer to the available unit, NULL if not found. - /// find an available unit for incoming packet. - /// @return Pointer to the available unit, NULL if not found. + CUnit* getNextAvailUnit(); - CUnit* getNextAvailUnit(); + void makeUnitFree(CUnit* unit); - void makeUnitFree(CUnit * unit); - - void makeUnitGood(CUnit * unit); + void makeUnitGood(CUnit* unit); public: - inline int getIPversion() const { return m_iIPversion; } + inline int getIPversion() const { return m_iIPversion; } private: - struct CQEntry - { - CUnit* m_pUnit; // unit queue - char* m_pBuffer; // data buffer - int m_iSize; // size of each queue + struct CQEntry + { + CUnit* m_pUnit; // unit queue + char* m_pBuffer; // data buffer + int m_iSize; // size of each queue - CQEntry* m_pNext; - } - *m_pQEntry, // pointer to the first unit queue - *m_pCurrQueue, // pointer to the current available queue - *m_pLastQueue; // pointer to the last unit queue + CQEntry* m_pNext; + } * m_pQEntry, // pointer to the first unit queue + *m_pCurrQueue, // pointer to the current available queue + *m_pLastQueue; // pointer to the last unit queue - CUnit* m_pAvailUnit; // recent available unit + CUnit* m_pAvailUnit; // recent available unit - int m_iSize; // total size of the unit queue, in number of packets - int m_iCount; // total number of valid (occupied) packets in the queue + int m_iSize; // total size of the unit queue, in number of packets + int m_iCount; // total number of valid (occupied) packets in the queue - int m_iMSS; // unit buffer size - int m_iIPversion; // IP version + int m_iMSS; // unit buffer size + int m_iIPversion; // IP version private: - CUnitQueue(const CUnitQueue&); - CUnitQueue& operator=(const CUnitQueue&); + CUnitQueue(const CUnitQueue&); + CUnitQueue& operator=(const CUnitQueue&); }; struct CSNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + srt::sync::steady_clock::time_point m_tsTimeStamp; - int m_iHeapLoc; // location on the heap, -1 means not on the heap + int m_iHeapLoc; // location on the heap, -1 means not on the heap }; class CSndUList { -friend class CSndQueue; + friend class CSndQueue; public: - CSndUList(); - ~CSndUList(); + CSndUList(); + ~CSndUList(); public: + enum EReschedule + { + DONT_RESCHEDULE = 0, + DO_RESCHEDULE = 1 + }; - enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 }; - - static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; } + static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; } - /// Update the timestamp of the UDT instance on the list. - /// @param [in] u pointer to the UDT instance - /// @param [in] reschedule if the timestamp should be rescheduled + /// Update the timestamp of the UDT instance on the list. + /// @param [in] u pointer to the UDT instance + /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); + void update(const CUDT* u, EReschedule reschedule); - /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. - /// @param [out] addr destination address of the next packet - /// @param [out] pkt the next packet to be sent - /// @return 1 if successfully retrieved, -1 if no packet found. + /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. + /// @param [out] addr destination address of the next packet + /// @param [out] pkt the next packet to be sent + /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); + int pop(sockaddr_any& addr, CPacket& pkt); - /// Remove UDT instance from the list. - /// @param [in] u pointer to the UDT instance + /// Remove UDT instance from the list. + /// @param [in] u pointer to the UDT instance - void remove(const CUDT* u); + void remove(const CUDT* u); - /// Retrieve the next scheduled processing time. - /// @return Scheduled processing time of the first UDT socket in the list. + /// Retrieve the next scheduled processing time. + /// @return Scheduled processing time of the first UDT socket in the list. - srt::sync::steady_clock::time_point getNextProcTime(); + srt::sync::steady_clock::time_point getNextProcTime(); private: + /// Doubles the size of the list. + /// + void realloc_(); - /// Doubles the size of the list. - /// - void realloc_(); + /// Insert a new UDT instance into the list with realloc if required. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); - /// Insert a new UDT instance into the list with realloc if required. - /// - /// @param [in] ts time stamp: next processing time - /// @param [in] u pointer to the UDT instance - void insert_(const srt::sync::steady_clock::time_point &ts, const CUDT* u); + /// Insert a new UDT instance into the list without realloc. + /// Should be called if there is a gauranteed space for the element. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_norealloc_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); - /// Insert a new UDT instance into the list without realloc. - /// Should be called if there is a gauranteed space for the element. - /// - /// @param [in] ts time stamp: next processing time - /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const srt::sync::steady_clock::time_point &ts, const CUDT* u); - - void remove_(const CUDT* u); + void remove_(const CUDT* u); private: - CSNode** m_pHeap; // The heap array - int m_iArrayLength; // physical length of the array - int m_iLastEntry; // position of last entry on the heap array + CSNode** m_pHeap; // The heap array + int m_iArrayLength; // physical length of the array + int m_iLastEntry; // position of last entry on the heap array - srt::sync::Mutex m_ListLock; + srt::sync::Mutex m_ListLock; - srt::sync::Mutex* m_pWindowLock; - srt::sync::Condition* m_pWindowCond; + srt::sync::Mutex* m_pWindowLock; + srt::sync::Condition* m_pWindowCond; - srt::sync::CTimer* m_pTimer; + srt::sync::CTimer* m_pTimer; private: - CSndUList(const CSndUList&); - CSndUList& operator=(const CSndUList&); + CSndUList(const CSndUList&); + CSndUList& operator=(const CSndUList&); }; struct CRNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp - CRNode* m_pPrev; // previous link - CRNode* m_pNext; // next link + CRNode* m_pPrev; // previous link + CRNode* m_pNext; // next link - bool m_bOnList; // if the node is already on the list + bool m_bOnList; // if the node is already on the list }; class CRcvUList { public: - CRcvUList(); - ~CRcvUList(); + CRcvUList(); + ~CRcvUList(); public: + /// Insert a new UDT instance to the list. + /// @param [in] u pointer to the UDT instance - /// Insert a new UDT instance to the list. - /// @param [in] u pointer to the UDT instance - - void insert(const CUDT* u); + void insert(const CUDT* u); - /// Remove the UDT instance from the list. - /// @param [in] u pointer to the UDT instance + /// Remove the UDT instance from the list. + /// @param [in] u pointer to the UDT instance - void remove(const CUDT* u); + void remove(const CUDT* u); - /// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. - /// @param [in] u pointer to the UDT instance + /// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. + /// @param [in] u pointer to the UDT instance - void update(const CUDT* u); + void update(const CUDT* u); public: - CRNode* m_pUList; // the head node + CRNode* m_pUList; // the head node private: - CRNode* m_pLast; // the last node + CRNode* m_pLast; // the last node private: - CRcvUList(const CRcvUList&); - CRcvUList& operator=(const CRcvUList&); + CRcvUList(const CRcvUList&); + CRcvUList& operator=(const CRcvUList&); }; class CHash { public: - CHash(); - ~CHash(); + CHash(); + ~CHash(); public: + /// Initialize the hash table. + /// @param [in] size hash table size - /// Initialize the hash table. - /// @param [in] size hash table size - - void init(int size); + void init(int size); - /// Look for a UDT instance from the hash table. - /// @param [in] id socket ID - /// @return Pointer to a UDT instance, or NULL if not found. + /// Look for a UDT instance from the hash table. + /// @param [in] id socket ID + /// @return Pointer to a UDT instance, or NULL if not found. - CUDT* lookup(int32_t id); + CUDT* lookup(int32_t id); - /// Insert an entry to the hash table. - /// @param [in] id socket ID - /// @param [in] u pointer to the UDT instance + /// Insert an entry to the hash table. + /// @param [in] id socket ID + /// @param [in] u pointer to the UDT instance - void insert(int32_t id, CUDT* u); + void insert(int32_t id, CUDT* u); - /// Remove an entry from the hash table. - /// @param [in] id socket ID + /// Remove an entry from the hash table. + /// @param [in] id socket ID - void remove(int32_t id); + void remove(int32_t id); private: - struct CBucket - { - int32_t m_iID; // Socket ID - CUDT* m_pUDT; // Socket instance + struct CBucket + { + int32_t m_iID; // Socket ID + CUDT* m_pUDT; // Socket instance - CBucket* m_pNext; // next bucket - } **m_pBucket; // list of buckets (the hash table) + CBucket* m_pNext; // next bucket + } * *m_pBucket; // list of buckets (the hash table) - int m_iHashSize; // size of hash table + int m_iHashSize; // size of hash table private: - CHash(const CHash&); - CHash& operator=(const CHash&); + CHash(const CHash&); + CHash& operator=(const CHash&); }; /// @brief A queue of sockets pending for connection. @@ -336,8 +337,7 @@ class CRendezvousQueue /// @param u pointer to a corresponding CUDT instance. /// @param addr remote address to connect to. /// @param ttl timepoint for connection attempt to expire. - void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, - const srt::sync::steady_clock::time_point &ttl); + void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const srt::sync::steady_clock::time_point& ttl); /// @brief Remove a socket from the connection pending list. /// @param id socket ID. @@ -359,257 +359,256 @@ class CRendezvousQueue private: struct LinkStatusInfo { - CUDT* u; - SRTSOCKET id; - int errorcode; + CUDT* u; + SRTSOCKET id; + int errorcode; sockaddr_any peeraddr; - int token; + int token; struct HasID { SRTSOCKET id; - HasID(SRTSOCKET p) : id(p) {} - bool operator()(const LinkStatusInfo& i) + HasID(SRTSOCKET p) + : id(p) { - return i.id == id; } + bool operator()(const LinkStatusInfo& i) { return i.id == id; } }; }; /// @brief Qualify pending connections: /// - Sockets with expired TTL go to the 'to_remove' list and removed from the queue straight away. /// - If HS request is to be resent (resend 250 ms if no response from the peer) go to the 'to_process' list. - /// + /// /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. /// @param iDstSockID destination socket ID of the received packet. /// @param[in,out] toRemove stores sockets with expired TTL. /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. - bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, int iDstSockID, - std::vector& toRemove, std::vector& toProcess); + bool qualifyToHandle(EReadStatus rst, + EConnectStatus cst, + int iDstSockID, + std::vector& toRemove, + std::vector& toProcess); private: - struct CRL - { - SRTSOCKET m_iID; // SRT socket ID (self) - CUDT* m_pUDT; // CUDT instance - sockaddr_any m_PeerAddr;// SRT sonnection peer address - srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires - }; - std::list m_lRendezvousID; // The sockets currently in rendezvous mode - - mutable srt::sync::Mutex m_RIDListLock; + struct CRL + { + SRTSOCKET m_iID; // SRT socket ID (self) + CUDT* m_pUDT; // CUDT instance + sockaddr_any m_PeerAddr; // SRT sonnection peer address + srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires + }; + std::list m_lRendezvousID; // The sockets currently in rendezvous mode + + mutable srt::sync::Mutex m_RIDListLock; }; class CSndQueue { -friend class CUDT; -friend class CUDTUnited; + friend class CUDT; + friend class CUDTUnited; public: - CSndQueue(); - ~CSndQueue(); + CSndQueue(); + ~CSndQueue(); public: + // XXX There's currently no way to access the socket ID set for + // whatever the queue is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the queue is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - /// Initialize the sending queue. - /// @param [in] c UDP channel to be associated to the queue - /// @param [in] t Timer + /// Initialize the sending queue. + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t Timer - void init(CChannel* c, srt::sync::CTimer* t); + void init(CChannel* c, srt::sync::CTimer* t); - /// Send out a packet to a given address. - /// @param [in] addr destination address - /// @param [in] packet packet to be sent out - /// @return Size of data sent out. + /// Send out a packet to a given address. + /// @param [in] addr destination address + /// @param [in] packet packet to be sent out + /// @return Size of data sent out. - int sendto(const sockaddr_any& addr, CPacket& packet); + int sendto(const sockaddr_any& addr, CPacket& packet); - /// Get the IP TTL. - /// @param [in] ttl IP Time To Live. - /// @return TTL. + /// Get the IP TTL. + /// @param [in] ttl IP Time To Live. + /// @return TTL. - int getIpTTL() const; + int getIpTTL() const; - /// Get the IP Type of Service. - /// @return ToS. + /// Get the IP Type of Service. + /// @return ToS. - int getIpToS() const; + int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE - bool getBind(char* dst, size_t len) const; + bool getBind(char* dst, size_t len) const; #endif - int ioctlQuery(int type) const; - int sockoptQuery(int level, int type) const; + int ioctlQuery(int type) const; + int sockoptQuery(int level, int type) const; - void setClosing() - { - m_bClosing = true; - } + void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; + static void* worker(void* param); + srt::sync::CThread m_WorkerThread; private: - CSndUList* m_pSndUList; // List of UDT instances for data sending - CChannel* m_pChannel; // The UDP channel for data sending - srt::sync::CTimer* m_pTimer; // Timing facility + CSndUList* m_pSndUList; // List of UDT instances for data sending + CChannel* m_pChannel; // The UDP channel for data sending + srt::sync::CTimer* m_pTimer; // Timing facility - srt::sync::Mutex m_WindowLock; - srt::sync::Condition m_WindowCond; + srt::sync::Mutex m_WindowLock; + srt::sync::Condition m_WindowCond; - volatile bool m_bClosing; // closing the worker + volatile bool m_bClosing; // closing the worker -#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker - uint64_t m_ullDbgPeriod; - uint64_t m_ullDbgTime; - struct { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker + uint64_t m_ullDbgPeriod; + uint64_t m_ullDbgTime; + struct + { unsigned long lIteration; // - unsigned long lSleepTo; //SleepTo - unsigned long lNotReadyPop; //Continue + unsigned long lSleepTo; // SleepTo + unsigned long lNotReadyPop; // Continue unsigned long lSendTo; - unsigned long lNotReadyTs; - unsigned long lCondWait; //block on m_WindowCond - } m_WorkerStats; + unsigned long lNotReadyTs; + unsigned long lCondWait; // block on m_WindowCond + } m_WorkerStats; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ #if ENABLE_LOGGING - static int m_counter; + static int m_counter; #endif private: - CSndQueue(const CSndQueue&); - CSndQueue& operator=(const CSndQueue&); + CSndQueue(const CSndQueue&); + CSndQueue& operator=(const CSndQueue&); }; class CRcvQueue { -friend class CUDT; -friend class CUDTUnited; + friend class CUDT; + friend class CUDTUnited; public: - CRcvQueue(); - ~CRcvQueue(); + CRcvQueue(); + ~CRcvQueue(); public: + // XXX There's currently no way to access the socket ID set for + // whatever the queue is currently working. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the queue is currently working. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - /// Initialize the receiving queue. - /// @param [in] size queue size - /// @param [in] mss maximum packet size - /// @param [in] version IP version - /// @param [in] hsize hash table size - /// @param [in] c UDP channel to be associated to the queue - /// @param [in] t timer + /// Initialize the receiving queue. + /// @param [in] size queue size + /// @param [in] mss maximum packet size + /// @param [in] version IP version + /// @param [in] hsize hash table size + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t timer - void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); + void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); - /// Read a packet for a specific UDT socket id. - /// @param [in] id Socket ID - /// @param [out] packet received packet - /// @return Data size of the packet + /// Read a packet for a specific UDT socket id. + /// @param [in] id Socket ID + /// @param [out] packet received packet + /// @return Data size of the packet - int recvfrom(int32_t id, CPacket& to_packet); + int recvfrom(int32_t id, CPacket& to_packet); - void stopWorker(); + void stopWorker(); - void setClosing() - { - m_bClosing = true; - } + void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; - // Subroutines of worker - EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); - EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); + static void* worker(void* param); + srt::sync::CThread m_WorkerThread; + // Subroutines of worker + EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); + EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); private: - CUnitQueue m_UnitQueue; // The received packet queue - CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue - CHash* m_pHash; // Hash table for UDT socket looking up - CChannel* m_pChannel; // UDP channel for receving packets - srt::sync::CTimer* m_pTimer; // shared timer with the snd queue + CUnitQueue m_UnitQueue; // The received packet queue + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + srt::sync::CTimer* m_pTimer; // shared timer with the snd queue - size_t m_szPayloadSize; // packet payload size + size_t m_szPayloadSize; // packet payload size - volatile bool m_bClosing; // closing the worker + volatile bool m_bClosing; // closing the worker #if ENABLE_LOGGING - static int m_counter; + static int m_counter; #endif private: - int setListener(CUDT* u); - void removeListener(const CUDT* u); + int setListener(CUDT* u); + void removeListener(const CUDT* u); - void registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const srt::sync::steady_clock::time_point& ttl); - void removeConnector(const SRTSOCKET& id); + void registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const srt::sync::steady_clock::time_point& ttl); + void removeConnector(const SRTSOCKET& id); - void setNewEntry(CUDT* u); - bool ifNewEntry(); - CUDT* getNewEntry(); + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); - void storePkt(int32_t id, CPacket* pkt); + void storePkt(int32_t id, CPacket* pkt); private: - srt::sync::Mutex m_LSLock; - CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity - CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + srt::sync::Mutex m_LSLock; + CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode - std::vector m_vNewEntry; // newly added entries, to be inserted - srt::sync::Mutex m_IDLock; + std::vector m_vNewEntry; // newly added entries, to be inserted + srt::sync::Mutex m_IDLock; - std::map > m_mBuffer; // temporary buffer for rendezvous connection request - srt::sync::Mutex m_BufferLock; - srt::sync::Condition m_BufferCond; + std::map > m_mBuffer; // temporary buffer for rendezvous connection request + srt::sync::Mutex m_BufferLock; + srt::sync::Condition m_BufferCond; private: - CRcvQueue(const CRcvQueue&); - CRcvQueue& operator=(const CRcvQueue&); + CRcvQueue(const CRcvQueue&); + CRcvQueue& operator=(const CRcvQueue&); }; struct CMultiplexer { - CSndQueue* m_pSndQueue; // The sending queue - CRcvQueue* m_pRcvQueue; // The receiving queue - CChannel* m_pChannel; // The UDP channel for sending and receiving - srt::sync::CTimer* m_pTimer; // The timer - - int m_iPort; // The UDP port number of this multiplexer - int m_iIPversion; // Address family (AF_INET or AF_INET6) - int m_iRefCount; // number of UDT instances that are associated with this multiplexer - - CSrtMuxerConfig m_mcfg; - - int m_iID; // multiplexer ID - - // Constructor should reset all pointers to NULL - // to prevent dangling pointer when checking for memory alloc fails - CMultiplexer() - : m_pSndQueue(NULL) - , m_pRcvQueue(NULL) - , m_pChannel(NULL) - , m_pTimer(NULL) + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + srt::sync::CTimer* m_pTimer; // The timer + + int m_iPort; // The UDP port number of this multiplexer + int m_iIPversion; // Address family (AF_INET or AF_INET6) + int m_iRefCount; // number of UDT instances that are associated with this multiplexer + + CSrtMuxerConfig m_mcfg; + + int m_iID; // multiplexer ID + + // Constructor should reset all pointers to NULL + // to prevent dangling pointer when checking for memory alloc fails + CMultiplexer() + : m_pSndQueue(NULL) + , m_pRcvQueue(NULL) + , m_pChannel(NULL) + , m_pTimer(NULL) { } - void destroy(); + void destroy(); }; #endif From 445a60c19cbda6dbf24087910f5b868d90a6bddc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 12:14:03 +0200 Subject: [PATCH 185/790] [core] Refax: placing SRT classes inside 'srt' namespace. CUDT, CUDTUnited, CUDTSocket, CUDTGroup, etc. --- srtcore/api.cpp | 370 ++++++++++++++++------------------- srtcore/api.h | 38 ++-- srtcore/buffer.cpp | 15 +- srtcore/buffer.h | 16 +- srtcore/channel.cpp | 43 ++-- srtcore/channel.h | 7 +- srtcore/common.cpp | 1 + srtcore/common.h | 12 +- srtcore/congctl.cpp | 1 + srtcore/congctl.h | 18 +- srtcore/core.cpp | 238 +++++++++++----------- srtcore/core.h | 27 +-- srtcore/crypto.cpp | 1 + srtcore/crypto.h | 14 +- srtcore/epoll.h | 13 +- srtcore/fec.cpp | 13 +- srtcore/fec.h | 4 + srtcore/group.cpp | 4 + srtcore/group.h | 62 +++--- srtcore/handshake.cpp | 25 +-- srtcore/packet.cpp | 68 ++++--- srtcore/packet.h | 8 +- srtcore/packetfilter.cpp | 44 +++-- srtcore/packetfilter.h | 4 + srtcore/packetfilter_api.h | 4 +- srtcore/queue.cpp | 134 ++++++------- srtcore/queue.h | 72 +++---- srtcore/socketconfig.h | 16 +- srtcore/srt_c_api.cpp | 1 + srtcore/udt.h | 2 +- srtcore/window.cpp | 4 +- srtcore/window.h | 8 +- test/test_buffer.cpp | 2 + test/test_fec_rebuilding.cpp | 21 +- test/test_seqno.cpp | 2 + test/test_unitqueue.cpp | 3 +- testing/testmedia.cpp | 2 +- 37 files changed, 686 insertions(+), 631 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 542832f77..44a068764 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -83,7 +83,7 @@ using namespace srt::sync; extern LogConfig srt_logger_config; -void CUDTSocket::construct() +void srt::CUDTSocket::construct() { #if ENABLE_EXPERIMENTAL_BONDING m_GroupOf = NULL; @@ -94,7 +94,7 @@ void CUDTSocket::construct() setupMutex(m_ControlLock, "Control"); } -CUDTSocket::~CUDTSocket() +srt::CUDTSocket::~CUDTSocket() { delete m_pUDT; @@ -106,7 +106,7 @@ CUDTSocket::~CUDTSocket() } -SRT_SOCKSTATUS CUDTSocket::getStatus() +SRT_SOCKSTATUS srt::CUDTSocket::getStatus() { // TTL in CRendezvousQueue::updateConnStatus() will set m_bConnecting to false. // Although m_Status is still SRTS_CONNECTING, the connection is in fact to be closed due to TTL expiry. @@ -124,7 +124,7 @@ SRT_SOCKSTATUS CUDTSocket::getStatus() } // [[using locked(m_GlobControlLock)]] -void CUDTSocket::breakSocket_LOCKED() +void srt::CUDTSocket::breakSocket_LOCKED() { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. @@ -135,7 +135,7 @@ void CUDTSocket::breakSocket_LOCKED() setClosed(); } -void CUDTSocket::setClosed() +void srt::CUDTSocket::setClosed() { m_Status = SRTS_CLOSED; @@ -146,14 +146,14 @@ void CUDTSocket::setClosed() m_tsClosureTimeStamp = steady_clock::now(); } -void CUDTSocket::setBrokenClosed() +void srt::CUDTSocket::setBrokenClosed() { m_pUDT->m_iBrokenCounter = 60; m_pUDT->m_bBroken = true; setClosed(); } -bool CUDTSocket::readReady() +bool srt::CUDTSocket::readReady() { if (m_pUDT->m_bConnected && m_pUDT->m_pRcvBuffer->isRcvDataReady()) return true; @@ -165,33 +165,33 @@ bool CUDTSocket::readReady() return broken(); } -bool CUDTSocket::writeReady() const +bool srt::CUDTSocket::writeReady() const { return (m_pUDT->m_bConnected && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) || broken(); } -bool CUDTSocket::broken() const +bool srt::CUDTSocket::broken() const { return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; } //////////////////////////////////////////////////////////////////////////////// -CUDTUnited::CUDTUnited(): -m_Sockets(), -m_GlobControlLock(), -m_IDLock(), -m_mMultiplexer(), -m_MultiplexerLock(), -m_pCache(NULL), -m_bClosing(false), -m_GCStopCond(), -m_InitLock(), -m_iInstanceCount(0), -m_bGCStatus(false), -m_ClosedSockets() +srt::CUDTUnited::CUDTUnited(): + m_Sockets(), + m_GlobControlLock(), + m_IDLock(), + m_mMultiplexer(), + m_MultiplexerLock(), + m_pCache(NULL), + m_bClosing(false), + m_GCStopCond(), + m_InitLock(), + m_iInstanceCount(0), + m_bGCStatus(false), + m_ClosedSockets() { // Socket ID MUST start from a random value m_SocketIDGenerator = genRandomInt(1, MAX_SOCKET_VAL); @@ -207,7 +207,7 @@ m_ClosedSockets() m_pCache = new CCache; } -CUDTUnited::~CUDTUnited() +srt::CUDTUnited::~CUDTUnited() { // Call it if it wasn't called already. // This will happen at the end of main() of the application, @@ -224,7 +224,7 @@ CUDTUnited::~CUDTUnited() delete m_pCache; } -std::string CUDTUnited::CONID(SRTSOCKET sock) +string srt::CUDTUnited::CONID(SRTSOCKET sock) { if ( sock == 0 ) return ""; @@ -234,7 +234,7 @@ std::string CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -int CUDTUnited::startup() +int srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); @@ -277,7 +277,7 @@ int CUDTUnited::startup() return 0; } -int CUDTUnited::cleanup() +int srt::CUDTUnited::cleanup() { // IMPORTANT!!! // In this function there must be NO LOGGING AT ALL. This function may @@ -324,7 +324,7 @@ int CUDTUnited::cleanup() return 0; } -SRTSOCKET CUDTUnited::generateSocketID(bool for_group) +SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) { ScopedLock guard(m_IDLock); @@ -426,7 +426,7 @@ SRTSOCKET CUDTUnited::generateSocketID(bool for_group) return sockval; } -SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) +SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) { // XXX consider using some replacement of std::unique_ptr // so that exceptions will clean up the object without the @@ -484,7 +484,7 @@ SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } -int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, +int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs, int& w_error, CUDT*& w_acpu) { CUDTSocket* ns = NULL; @@ -852,12 +852,12 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, } // static forwarder -int CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return s_UDTUnited.installAcceptHook(lsn, hook, opaq); } -int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { try { @@ -873,12 +873,12 @@ int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* h return 0; } -int CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return s_UDTUnited.installConnectHook(lsn, hook, opaq); } -int CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) +int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) { try { @@ -902,7 +902,7 @@ int CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* h return 0; } -SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u) +SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) { // protects the m_Sockets structure ScopedLock cg(m_GlobControlLock); @@ -919,7 +919,7 @@ SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } -int CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) +int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -937,7 +937,7 @@ int CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) return 0; } -int CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) +int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) { ScopedLock cg(s->m_ControlLock); @@ -966,7 +966,7 @@ int CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) return 0; } -int CUDTUnited::listen(const SRTSOCKET u, int backlog) +int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) { if (backlog <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -1012,7 +1012,7 @@ int CUDTUnited::listen(const SRTSOCKET u, int backlog) return 0; } -SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) +SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) { CEPollDesc* ed = 0; int eid = m_EPoll.create(&ed); @@ -1056,7 +1056,7 @@ SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64 return accept(lsn, ((sockaddr*)&dummy), (&outlen)); } -SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) +SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) { if (pw_addr && !pw_addrlen) { @@ -1190,7 +1190,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ return u; } -int CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) +int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified if (!srcname || !tarname || size_t(namelen) < sizeof (sockaddr_in)) @@ -1235,7 +1235,7 @@ int CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* ta return connectIn(s, target_addr, SRT_SEQNO_NONE); } -int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { sockaddr_any target_addr(name, namelen); if (target_addr.len == 0) @@ -1266,7 +1266,7 @@ int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, in } #if ENABLE_EXPERIMENTAL_BONDING -int CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) +int srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) { int gstat = groupConnect(pg, gd, 1); if (gstat == -1) @@ -1286,7 +1286,7 @@ int CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) } // [[using assert(pg->m_iBusy > 0)]] -int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) +int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) { CUDTGroup& g = *pg; SRT_ASSERT(g.m_iBusy > 0); @@ -1822,7 +1822,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar #endif -int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) +int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) { ScopedLock cg(s->m_ControlLock); // a socket can "connect" only if it is in the following states: @@ -1892,7 +1892,7 @@ int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_ } -int CUDTUnited::close(const SRTSOCKET u) +int srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) @@ -1911,7 +1911,7 @@ int CUDTUnited::close(const SRTSOCKET u) } #if ENABLE_EXPERIMENTAL_BONDING -void CUDTUnited::deleteGroup(CUDTGroup* g) +void srt::CUDTUnited::deleteGroup(CUDTGroup* g) { using srt_logging::gmlog; @@ -1920,7 +1920,7 @@ void CUDTUnited::deleteGroup(CUDTGroup* g) } // [[using locked(m_GlobControlLock)]] -void CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) +void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) { SRT_ASSERT(g->groupEmpty()); @@ -1960,7 +1960,7 @@ void CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int CUDTUnited::close(CUDTSocket* s) +int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); @@ -2118,7 +2118,7 @@ int CUDTUnited::close(CUDTSocket* s) return 0; } -void CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) +void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) { if (!pw_name || !pw_namelen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -2142,7 +2142,7 @@ void CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namel *pw_namelen = len; } -void CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) +void srt::CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) { if (!pw_name || !pw_namelen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -2166,7 +2166,7 @@ void CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namel *pw_namelen = len; } -int CUDTUnited::select( +int srt::CUDTUnited::select( UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout) { const steady_clock::time_point entertime = steady_clock::now(); @@ -2276,7 +2276,7 @@ int CUDTUnited::select( return count; } -int CUDTUnited::selectEx( +int srt::CUDTUnited::selectEx( const vector& fds, vector* readfds, vector* writefds, @@ -2350,17 +2350,17 @@ int CUDTUnited::selectEx( return count; } -int CUDTUnited::epoll_create() +int srt::CUDTUnited::epoll_create() { return m_EPoll.create(); } -int CUDTUnited::epoll_clear_usocks(int eid) +int srt::CUDTUnited::epoll_clear_usocks(int eid) { return m_EPoll.clear_usocks(eid); } -int CUDTUnited::epoll_add_usock( +int srt::CUDTUnited::epoll_add_usock( const int eid, const SRTSOCKET u, const int* events) { int ret = -1; @@ -2390,27 +2390,27 @@ int CUDTUnited::epoll_add_usock( // NOTE: WILL LOCK (serially): // - CEPoll::m_EPollLock // - CUDT::m_RecvLock -int CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) +int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) { int ret = m_EPoll.update_usock(eid, s->m_SocketID, events); s->m_pUDT->addEPoll(eid); return ret; } -int CUDTUnited::epoll_add_ssock( +int srt::CUDTUnited::epoll_add_ssock( const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } -int CUDTUnited::epoll_update_ssock( +int srt::CUDTUnited::epoll_update_ssock( const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.update_ssock(eid, s, events); } template -int CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) +int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) { // XXX Not sure if this is anyhow necessary because setting readiness // to false doesn't actually trigger any action. Further research needed. @@ -2432,19 +2432,19 @@ int CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) } // Needed internal access! -int CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) +int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { return epoll_remove_entity(eid, s->m_pUDT); } #if ENABLE_EXPERIMENTAL_BONDING -int CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) +int srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) { return epoll_remove_entity(eid, g); } #endif -int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { CUDTSocket* s = 0; @@ -2470,12 +2470,12 @@ int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) return m_EPoll.update_usock(eid, u, &no_events); } -int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +int srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } -int CUDTUnited::epoll_uwait( +int srt::CUDTUnited::epoll_uwait( const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, @@ -2484,17 +2484,17 @@ int CUDTUnited::epoll_uwait( return m_EPoll.uwait(eid, fdsSet, fdsSize, msTimeOut); } -int32_t CUDTUnited::epoll_set(int eid, int32_t flags) +int32_t srt::CUDTUnited::epoll_set(int eid, int32_t flags) { return m_EPoll.setflags(eid, flags); } -int CUDTUnited::epoll_release(const int eid) +int srt::CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } -CUDTSocket* CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) +srt::CUDTSocket* srt::CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) { ScopedLock cg (m_GlobControlLock); CUDTSocket* s = locateSocket_LOCKED(u); @@ -2509,7 +2509,7 @@ CUDTSocket* CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) } // [[using locked(m_GlobControlLock)]]; -CUDTSocket* CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) +srt::CUDTSocket* srt::CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) { sockets_t::iterator i = m_Sockets.find(u); @@ -2522,7 +2522,7 @@ CUDTSocket* CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) } #if ENABLE_EXPERIMENTAL_BONDING -CUDTGroup* CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) +srt::CUDTGroup* srt::CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) { ScopedLock cg (m_GlobControlLock); @@ -2539,7 +2539,7 @@ CUDTGroup* CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) return i->second; } -CUDTGroup* CUDTUnited::acquireSocketsGroup(CUDTSocket* s) +srt::CUDTGroup* srt::CUDTUnited::acquireSocketsGroup(CUDTSocket* s) { ScopedLock cg (m_GlobControlLock); CUDTGroup* g = s->m_GroupOf; @@ -2553,7 +2553,7 @@ CUDTGroup* CUDTUnited::acquireSocketsGroup(CUDTSocket* s) } #endif -CUDTSocket* CUDTUnited::locatePeer( +srt::CUDTSocket* srt::CUDTUnited::locatePeer( const sockaddr_any& peer, const SRTSOCKET id, int32_t isn) @@ -2582,7 +2582,7 @@ CUDTSocket* CUDTUnited::locatePeer( return NULL; } -void CUDTUnited::checkBrokenSockets() +void srt::CUDTUnited::checkBrokenSockets() { ScopedLock cg(m_GlobControlLock); @@ -2724,7 +2724,7 @@ void CUDTUnited::checkBrokenSockets() } // [[using locked(m_GlobControlLock)]] -void CUDTUnited::removeSocket(const SRTSOCKET u) +void srt::CUDTUnited::removeSocket(const SRTSOCKET u) { sockets_t::iterator i = m_ClosedSockets.find(u); @@ -2829,7 +2829,7 @@ void CUDTUnited::removeSocket(const SRTSOCKET u) } } -void CUDTUnited::updateMux( +void srt::CUDTUnited::updateMux( CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) { ScopedLock cg(m_GlobControlLock); @@ -2943,7 +2943,7 @@ void CUDTUnited::updateMux( // exists, otherwise the dispatching procedure wouldn't even call this // function. By historical reasons there's also a fallback for a case when the // multiplexer wasn't found by id, the search by port number continues. -bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) +bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) { ScopedLock cg(m_GlobControlLock); const int port = ls->m_SelfAddr.hport(); @@ -3026,7 +3026,7 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) return false; } -void* CUDTUnited::garbageCollect(void* p) +void* srt::CUDTUnited::garbageCollect(void* p) { CUDTUnited* self = (CUDTUnited*)p; @@ -3108,17 +3108,17 @@ void* CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -int CUDT::startup() +int srt::CUDT::startup() { return s_UDTUnited.startup(); } -int CUDT::cleanup() +int srt::CUDT::cleanup() { return s_UDTUnited.cleanup(); } -SRTSOCKET CUDT::socket() +SRTSOCKET srt::CUDT::socket() { if (!s_UDTUnited.m_bGCStatus) s_UDTUnited.startup(); @@ -3147,12 +3147,12 @@ SRTSOCKET CUDT::socket() } } -CUDT::APIError::APIError(const CUDTException& e) +srt::CUDT::APIError::APIError(const CUDTException& e) { SetThreadLocalError(e); } -CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) +srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) { SetThreadLocalError(CUDTException(mj, mn, syserr)); } @@ -3162,7 +3162,7 @@ CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) // This doesn't have argument of GroupType due to header file conflicts. // [[using locked(s_UDTUnited.m_GlobControlLock)]] -CUDTGroup& CUDT::newGroup(const int type) +srt::CUDTGroup& srt::CUDT::newGroup(const int type) { const SRTSOCKET id = s_UDTUnited.generateSocketID(true); @@ -3170,7 +3170,7 @@ CUDTGroup& CUDT::newGroup(const int type) return s_UDTUnited.addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); } -SRTSOCKET CUDT::createGroup(SRT_GROUP_TYPE gt) +SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() if (!s_UDTUnited.m_bGCStatus) @@ -3199,7 +3199,7 @@ SRTSOCKET CUDT::createGroup(SRT_GROUP_TYPE gt) } -int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) +int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) { // Check if socket and group have been set correctly. int32_t sid = socket & ~SRTGROUP_MASK; @@ -3253,7 +3253,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) // dead function as for now. This is only for non-managed // groups. -int CUDT::removeSocketFromGroup(SRTSOCKET socket) +int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) { CUDTSocket* s = s_UDTUnited.locateSocket(socket); if (!s) @@ -3270,7 +3270,7 @@ int CUDT::removeSocketFromGroup(SRTSOCKET socket) // [[using locked(m_ControlLock)]] // [[using locked(CUDT::s_UDTUnited.m_GlobControlLock)]] -void CUDTSocket::removeFromGroup(bool broken) +void srt::CUDTSocket::removeFromGroup(bool broken) { CUDTGroup* g = m_GroupOf; if (g) @@ -3299,7 +3299,7 @@ void CUDTSocket::removeFromGroup(bool broken) } } -SRTSOCKET CUDT::getGroupOfSocket(SRTSOCKET socket) +SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) { // Lock this for the whole function as we need the group // to persist the call. @@ -3311,7 +3311,7 @@ SRTSOCKET CUDT::getGroupOfSocket(SRTSOCKET socket) return s->m_GroupOf->id(); } -int CUDT::configureGroup(SRTSOCKET groupid, const char* str) +int srt::CUDT::configureGroup(SRTSOCKET groupid, const char* str) { if ( (groupid & SRTGROUP_MASK) == 0) { @@ -3327,7 +3327,7 @@ int CUDT::configureGroup(SRTSOCKET groupid, const char* str) return k.group->configure(str); } -int CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) +int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { if ((groupid & SRTGROUP_MASK) == 0 || !psize) { @@ -3345,7 +3345,7 @@ int CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psiz } #endif -int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) { try { @@ -3381,7 +3381,7 @@ int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) } } -int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { @@ -3407,7 +3407,7 @@ int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) } } -int CUDT::listen(SRTSOCKET u, int backlog) +int srt::CUDT::listen(SRTSOCKET u, int backlog) { try { @@ -3429,7 +3429,7 @@ int CUDT::listen(SRTSOCKET u, int backlog) } } -SRTSOCKET CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) +SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) { try { @@ -3454,7 +3454,7 @@ SRTSOCKET CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msT } } -SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) +SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) { try { @@ -3479,7 +3479,7 @@ SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) } } -int CUDT::connect( +int srt::CUDT::connect( SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) { try @@ -3503,7 +3503,7 @@ int CUDT::connect( } #if ENABLE_EXPERIMENTAL_BONDING -int CUDT::connectLinks(SRTSOCKET grp, +int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets [], int arraysize) { if (arraysize <= 0) @@ -3537,7 +3537,7 @@ int CUDT::connectLinks(SRTSOCKET grp, } #endif -int CUDT::connect( +int srt::CUDT::connect( SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { try @@ -3560,7 +3560,7 @@ int CUDT::connect( } } -int CUDT::close(SRTSOCKET u) +int srt::CUDT::close(SRTSOCKET u) { try { @@ -3578,7 +3578,7 @@ int CUDT::close(SRTSOCKET u) } } -int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { @@ -3597,7 +3597,7 @@ int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) } } -int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { @@ -3616,7 +3616,7 @@ int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } -int CUDT::getsockopt( +int srt::CUDT::getsockopt( SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) @@ -3651,7 +3651,7 @@ int CUDT::getsockopt( } } -int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { if (!optval) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3683,7 +3683,7 @@ int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, } } -int CUDT::send(SRTSOCKET u, const char* buf, int len, int) +int srt::CUDT::send(SRTSOCKET u, const char* buf, int len, int) { SRT_MSGCTRL mctrl = srt_msgctrl_default; return sendmsg2(u, buf, len, (mctrl)); @@ -3691,7 +3691,7 @@ int CUDT::send(SRTSOCKET u, const char* buf, int len, int) // --> CUDT::recv moved down -int CUDT::sendmsg( +int srt::CUDT::sendmsg( SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, int64_t srctime) { @@ -3702,7 +3702,7 @@ int CUDT::sendmsg( return sendmsg2(u, buf, len, (mctrl)); } -int CUDT::sendmsg2( +int srt::CUDT::sendmsg2( SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) { try @@ -3733,14 +3733,14 @@ int CUDT::sendmsg2( } } -int CUDT::recv(SRTSOCKET u, char* buf, int len, int) +int srt::CUDT::recv(SRTSOCKET u, char* buf, int len, int) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int ret = recvmsg2(u, buf, len, (mctrl)); return ret; } -int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) +int srt::CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int ret = recvmsg2(u, buf, len, (mctrl)); @@ -3748,7 +3748,7 @@ int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) return ret; } -int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) +int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) { try { @@ -3774,7 +3774,7 @@ int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) } } -int64_t CUDT::sendfile( +int64_t srt::CUDT::sendfile( SRTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) { try @@ -3798,7 +3798,7 @@ int64_t CUDT::sendfile( } } -int64_t CUDT::recvfile( +int64_t srt::CUDT::recvfile( SRTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) { try @@ -3817,7 +3817,7 @@ int64_t CUDT::recvfile( } } -int CUDT::select( +int srt::CUDT::select( int, UDT::UDSET* readfds, UDT::UDSET* writefds, @@ -3849,7 +3849,7 @@ int CUDT::select( } } -int CUDT::selectEx( +int srt::CUDT::selectEx( const vector& fds, vector* readfds, vector* writefds, @@ -3881,7 +3881,7 @@ int CUDT::selectEx( } } -int CUDT::epoll_create() +int srt::CUDT::epoll_create() { try { @@ -3899,7 +3899,7 @@ int CUDT::epoll_create() } } -int CUDT::epoll_clear_usocks(int eid) +int srt::CUDT::epoll_clear_usocks(int eid) { try { @@ -3917,7 +3917,7 @@ int CUDT::epoll_clear_usocks(int eid) } } -int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { try { @@ -3935,7 +3935,7 @@ int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) } } -int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { @@ -3953,7 +3953,7 @@ int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) } } -int CUDT::epoll_update_usock( +int srt::CUDT::epoll_update_usock( const int eid, const SRTSOCKET u, const int* events) { try @@ -3972,7 +3972,7 @@ int CUDT::epoll_update_usock( } } -int CUDT::epoll_update_ssock( +int srt::CUDT::epoll_update_ssock( const int eid, const SYSSOCKET s, const int* events) { try @@ -3992,7 +3992,7 @@ int CUDT::epoll_update_ssock( } -int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { @@ -4010,7 +4010,7 @@ int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) } } -int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { @@ -4028,7 +4028,7 @@ int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) } } -int CUDT::epoll_wait( +int srt::CUDT::epoll_wait( const int eid, set* readfds, set* writefds, @@ -4053,7 +4053,7 @@ int CUDT::epoll_wait( } } -int CUDT::epoll_uwait( +int srt::CUDT::epoll_uwait( const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, @@ -4075,7 +4075,7 @@ int CUDT::epoll_uwait( } } -int32_t CUDT::epoll_set( +int32_t srt::CUDT::epoll_set( const int eid, int32_t flags) { @@ -4095,7 +4095,7 @@ int32_t CUDT::epoll_set( } } -int CUDT::epoll_release(const int eid) +int srt::CUDT::epoll_release(const int eid) { try { @@ -4113,12 +4113,12 @@ int CUDT::epoll_release(const int eid) } } -CUDTException& CUDT::getlasterror() +CUDTException& srt::CUDT::getlasterror() { return GetThreadLocalError(); } -int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) @@ -4144,7 +4144,7 @@ int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous } #if ENABLE_EXPERIMENTAL_BONDING -int CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) +int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { @@ -4167,7 +4167,7 @@ int CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) } #endif -CUDT* CUDT::getUDTHandle(SRTSOCKET u) +srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { @@ -4187,7 +4187,7 @@ CUDT* CUDT::getUDTHandle(SRTSOCKET u) } } -vector CUDT::existingSockets() +vector srt::CUDT::existingSockets() { vector out; for (CUDTUnited::sockets_t::iterator i = s_UDTUnited.m_Sockets.begin(); @@ -4198,7 +4198,7 @@ vector CUDT::existingSockets() return out; } -SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u) +SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) { try { @@ -4225,7 +4225,6 @@ SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u) } } - //////////////////////////////////////////////////////////////////////////////// namespace UDT @@ -4233,64 +4232,64 @@ namespace UDT int startup() { - return CUDT::startup(); + return srt::CUDT::startup(); } int cleanup() { - return CUDT::cleanup(); + return srt::CUDT::cleanup(); } int bind(SRTSOCKET u, const struct sockaddr* name, int namelen) { - return CUDT::bind(u, name, namelen); + return srt::CUDT::bind(u, name, namelen); } int bind2(SRTSOCKET u, UDPSOCKET udpsock) { - return CUDT::bind(u, udpsock); + return srt::CUDT::bind(u, udpsock); } int listen(SRTSOCKET u, int backlog) { - return CUDT::listen(u, backlog); + return srt::CUDT::listen(u, backlog); } SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen) { - return CUDT::accept(u, addr, addrlen); + return srt::CUDT::accept(u, addr, addrlen); } int connect(SRTSOCKET u, const struct sockaddr* name, int namelen) { - return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); + return srt::CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } int close(SRTSOCKET u) { - return CUDT::close(u); + return srt::CUDT::close(u); } int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) { - return CUDT::getpeername(u, name, namelen); + return srt::CUDT::getpeername(u, name, namelen); } int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen) { - return CUDT::getsockname(u, name, namelen); + return srt::CUDT::getsockname(u, name, namelen); } int getsockopt( SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen) { - return CUDT::getsockopt(u, level, optname, optval, optlen); + return srt::CUDT::getsockopt(u, level, optname, optval, optlen); } int setsockopt( SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen) { - return CUDT::setsockopt(u, level, optname, optval, optlen); + return srt::CUDT::setsockopt(u, level, optname, optval, optlen); } // DEVELOPER API @@ -4298,17 +4297,17 @@ int setsockopt( int connect_debug( SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn) { - return CUDT::connect(u, name, namelen, forced_isn); + return srt::CUDT::connect(u, name, namelen, forced_isn); } int send(SRTSOCKET u, const char* buf, int len, int flags) { - return CUDT::send(u, buf, len, flags); + return srt::CUDT::send(u, buf, len, flags); } int recv(SRTSOCKET u, char* buf, int len, int flags) { - return CUDT::recv(u, buf, len, flags); + return srt::CUDT::recv(u, buf, len, flags); } @@ -4316,18 +4315,18 @@ int sendmsg( SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, int64_t srctime) { - return CUDT::sendmsg(u, buf, len, ttl, inorder, srctime); + return srt::CUDT::sendmsg(u, buf, len, ttl, inorder, srctime); } int recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) { - return CUDT::recvmsg(u, buf, len, srctime); + return srt::CUDT::recvmsg(u, buf, len, srctime); } int recvmsg(SRTSOCKET u, char* buf, int len) { int64_t srctime; - return CUDT::recvmsg(u, buf, len, srctime); + return srt::CUDT::recvmsg(u, buf, len, srctime); } int64_t sendfile( @@ -4337,7 +4336,7 @@ int64_t sendfile( int64_t size, int block) { - return CUDT::sendfile(u, ifs, offset, size, block); + return srt::CUDT::sendfile(u, ifs, offset, size, block); } int64_t recvfile( @@ -4347,7 +4346,7 @@ int64_t recvfile( int64_t size, int block) { - return CUDT::recvfile(u, ofs, offset, size, block); + return srt::CUDT::recvfile(u, ofs, offset, size, block); } int64_t sendfile2( @@ -4358,7 +4357,7 @@ int64_t sendfile2( int block) { fstream ifs(path, ios::binary | ios::in); - int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + int64_t ret = srt::CUDT::sendfile(u, ifs, *offset, size, block); ifs.close(); return ret; } @@ -4371,7 +4370,7 @@ int64_t recvfile2( int block) { fstream ofs(path, ios::binary | ios::out); - int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + int64_t ret = srt::CUDT::recvfile(u, ofs, *offset, size, block); ofs.close(); return ret; } @@ -4383,7 +4382,7 @@ int select( UDSET* exceptfds, const struct timeval* timeout) { - return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); + return srt::CUDT::select(nfds, readfds, writefds, exceptfds, timeout); } int selectEx( @@ -4393,47 +4392,47 @@ int selectEx( vector* exceptfds, int64_t msTimeOut) { - return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + return srt::CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } int epoll_create() { - return CUDT::epoll_create(); + return srt::CUDT::epoll_create(); } int epoll_clear_usocks(int eid) { - return CUDT::epoll_clear_usocks(eid); + return srt::CUDT::epoll_clear_usocks(eid); } int epoll_add_usock(int eid, SRTSOCKET u, const int* events) { - return CUDT::epoll_add_usock(eid, u, events); + return srt::CUDT::epoll_add_usock(eid, u, events); } int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) { - return CUDT::epoll_add_ssock(eid, s, events); + return srt::CUDT::epoll_add_ssock(eid, s, events); } int epoll_update_usock(int eid, SRTSOCKET u, const int* events) { - return CUDT::epoll_update_usock(eid, u, events); + return srt::CUDT::epoll_update_usock(eid, u, events); } int epoll_update_ssock(int eid, SYSSOCKET s, const int* events) { - return CUDT::epoll_update_ssock(eid, s, events); + return srt::CUDT::epoll_update_ssock(eid, s, events); } int epoll_remove_usock(int eid, SRTSOCKET u) { - return CUDT::epoll_remove_usock(eid, u); + return srt::CUDT::epoll_remove_usock(eid, u); } int epoll_remove_ssock(int eid, SYSSOCKET s) { - return CUDT::epoll_remove_ssock(eid, s); + return srt::CUDT::epoll_remove_ssock(eid, s); } int epoll_wait( @@ -4444,34 +4443,9 @@ int epoll_wait( set* lrfds, set* lwfds) { - return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); + return srt::CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); } -/* - -#define SET_RESULT(val, num, fds, it) \ - if (val != NULL) \ - { \ - if (val->empty()) \ - { \ - if (num) *num = 0; \ - } \ - else \ - { \ - if (*num > static_cast(val->size())) \ - *num = val->size(); \ - int count = 0; \ - for (it = val->begin(); it != val->end(); ++ it) \ - { \ - if (count >= *num) \ - break; \ - fds[count ++] = *it; \ - } \ - } \ - } - -*/ - template inline void set_result(set* val, int* num, SOCKTYPE* fds) { @@ -4524,7 +4498,7 @@ int epoll_wait2( if ((lwfds != NULL) && (lwnum != NULL)) lwval = &lwset; - int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); + int ret = srt::CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); if (ret > 0) { //set::const_iterator i; @@ -4544,32 +4518,32 @@ int epoll_wait2( int epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut) { - return CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); + return srt::CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); } int epoll_release(int eid) { - return CUDT::epoll_release(eid); + return srt::CUDT::epoll_release(eid); } ERRORINFO& getlasterror() { - return CUDT::getlasterror(); + return srt::CUDT::getlasterror(); } int getlasterror_code() { - return CUDT::getlasterror().getErrorCode(); + return srt::CUDT::getlasterror().getErrorCode(); } const char* getlasterror_desc() { - return CUDT::getlasterror().getErrorMessage(); + return srt::CUDT::getlasterror().getErrorMessage(); } int getlasterror_errno() { - return CUDT::getlasterror().getErrno(); + return srt::CUDT::getlasterror().getErrno(); } // Get error string of a given error code @@ -4581,12 +4555,12 @@ const char* geterror_desc(int code, int err) int bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear) { - return CUDT::bstats(u, perf, clear); + return srt::CUDT::bstats(u, perf, clear); } SRT_SOCKSTATUS getsockstate(SRTSOCKET u) { - return CUDT::getsockstate(u); + return srt::CUDT::getsockstate(u); } } // namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index 4ca299b24..3ef75c6e7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -72,6 +72,8 @@ modified by // Please refer to structure and locking information provided in the // docs/dev/low-level-info.md document. +namespace srt { + class CUDT; class CUDTSocket @@ -107,7 +109,7 @@ class CUDTSocket /// of sockets in order to prevent other methods from accessing invalid address. /// A timer is started and the socket will be removed after approximately /// 1 second (see CUDTUnited::checkBrokenSockets()). - srt::sync::steady_clock::time_point m_tsClosureTimeStamp; + sync::steady_clock::time_point m_tsClosureTimeStamp; sockaddr_any m_SelfAddr; //< local address of the socket sockaddr_any m_PeerAddr; //< peer address of the socket @@ -117,7 +119,7 @@ class CUDTSocket SRTSOCKET m_PeerID; //< peer socket ID #if ENABLE_EXPERIMENTAL_BONDING - srt::groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member + groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't #endif @@ -125,10 +127,10 @@ class CUDTSocket CUDT* m_pUDT; //< pointer to the UDT entity - std::set m_QueuedSockets; //< set of connections waiting for accept() + std::set m_QueuedSockets; //< set of connections waiting for accept() - srt::sync::Condition m_AcceptCond; //< used to block "accept" call - srt::sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond + sync::Condition m_AcceptCond; //< used to block "accept" call + sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond unsigned int m_uiBackLog; //< maximum number of connections in queue @@ -143,9 +145,9 @@ class CUDTSocket // When deleting, you simply "unsubscribe" yourself from the multiplexer, which // will unref it and remove the list element by the iterator kept by the // socket. - int m_iMuxID; //< multiplexer ID + int m_iMuxID; //< multiplexer ID - srt::sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect + sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect CUDT& core() { return *m_pUDT; } @@ -346,12 +348,12 @@ friend class CRendezvousQueue; groups_t m_Groups; #endif - srt::sync::Mutex m_GlobControlLock; // used to synchronize UDT API + sync::Mutex m_GlobControlLock; // used to synchronize UDT API - srt::sync::Mutex m_IDLock; // used to synchronize ID generation + sync::Mutex m_IDLock; // used to synchronize ID generation - SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID - SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one + SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID + SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one std::map > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn @@ -394,7 +396,7 @@ friend class CRendezvousQueue; // We have a guarantee that if `group` was set // as non-NULL here, it is also acquired and will not // be deleted until this busy flag is set back to false. - srt::sync::ScopedLock cgroup (*group->exp_groupLock()); + sync::ScopedLock cgroup (*group->exp_groupLock()); group->apiRelease(); // Only now that the group lock is lifted, can the // group be now deleted and this pointer potentially dangling @@ -408,21 +410,21 @@ friend class CRendezvousQueue; private: std::map m_mMultiplexer; // UDP multiplexer - srt::sync::Mutex m_MultiplexerLock; + sync::Mutex m_MultiplexerLock; private: CCache* m_pCache; // UDT network information cache private: volatile bool m_bClosing; - srt::sync::Mutex m_GCStopLock; - srt::sync::Condition m_GCStopCond; + sync::Mutex m_GCStopLock; + sync::Condition m_GCStopCond; - srt::sync::Mutex m_InitLock; + sync::Mutex m_InitLock; int m_iInstanceCount; // number of startup() called by application bool m_bGCStatus; // if the GC thread is working (true) - srt::sync::CThread m_GCThread; + sync::CThread m_GCThread; static void* garbageCollect(void*); sockets_t m_ClosedSockets; // temporarily store closed sockets @@ -440,4 +442,6 @@ friend class CRendezvousQueue; CUDTUnited& operator=(const CUDTUnited&); }; +} // namespace srt + #endif diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index bce9922b6..5a756792e 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -61,6 +61,7 @@ modified by using namespace std; using namespace srt_logging; +using namespace srt; using namespace srt::sync; // You can change this value at build config by using "ENFORCE" options. @@ -317,7 +318,7 @@ void CSndBuffer::updateInputRate(const steady_clock::time_point& time, int pkts, if (early_update || period_us > m_InRatePeriod) { // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBytesCount += (m_iInRatePktsCount * srt::CPacket::SRT_DATA_HDR_SIZE); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount @@ -410,7 +411,7 @@ steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& bloc return block.m_tsOriginTime; } -int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read if (m_pCurrBlock == m_pLastBlock) @@ -511,7 +512,7 @@ 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, srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) { int32_t& msgno_bitset = w_packet.m_iMsgNo; @@ -927,7 +928,7 @@ int CRcvBuffer::readBuffer(char* data, int len) return -1; } - const CPacket& pkt = m_pUnit[p]->m_Packet; + const srt::CPacket& pkt = m_pUnit[p]->m_Packet; if (bTsbPdEnabled) { @@ -996,7 +997,7 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) continue; } - const CPacket& pkt = m_pUnit[p]->m_Packet; + const srt::CPacket& pkt = m_pUnit[p]->m_Packet; #if ENABLE_LOGGING trace_seq = pkt.getSeqNo(); @@ -1436,7 +1437,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& if (m_tsbpd.isEnabled()) { - const CPacket* pkt = getRcvReadyPacket(seqdistance); + const srt::CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) { HLOGC(brlog.Debug, log << "isRcvDataReady: packet NOT extracted."); @@ -1573,7 +1574,7 @@ void CRcvBuffer::reportBufferStats() const uint64_t lower_time = low_ts; if (lower_time > upper_time) - upper_time += uint64_t(CPacket::MAX_TIMESTAMP) + 1; + upper_time += uint64_t(srt::CPacket::MAX_TIMESTAMP) + 1; int32_t timespan = upper_time - lower_time; int seqspan = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 7f7680b8d..9a92e25b0 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -145,7 +145,7 @@ class CSndBuffer /// @param [out] origintime origin time stamp of the message /// @param [in] kflags Odd|Even crypto key flag /// @return Actual length of data read. - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(srt::CPacket& w_packet, time_point& w_origintime, int kflgs); /// Find data position to pack a DATA packet for a retransmission. /// @param [out] data the pointer to the data position. @@ -154,7 +154,7 @@ class CSndBuffer /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message /// @return Actual length of data read. - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// 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) @@ -288,7 +288,7 @@ class CRcvBuffer /// Construct the buffer. /// @param [in] queue CUnitQueue that actually holds the units (packets) /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + CRcvBuffer(srt::CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); ~CRcvBuffer(); public: @@ -296,7 +296,7 @@ class CRcvBuffer /// @param [in] unit pointer to a data unit containing new packet /// @param [in] offset offset from last ACK point. /// @return 0 is success, -1 if data is repeated. - int addData(CUnit* unit, int offset); + int addData(srt::CUnit* unit, int offset); /// Read data into a user buffer. /// @param [in] data pointer to user buffer. @@ -402,7 +402,7 @@ class CRcvBuffer bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } - CPacket* getRcvReadyPacket(int32_t seqdistance); + srt::CPacket* getRcvReadyPacket(int32_t seqdistance); /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay @@ -462,7 +462,7 @@ class CRcvBuffer /// data. size_t freeUnitAt(size_t p) { - CUnit* u = m_pUnit[p]; + srt::CUnit* u = m_pUnit[p]; m_pUnit[p] = NULL; size_t rmbytes = u->m_Packet.getLength(); m_pUnitQueue->makeUnitFree(u); @@ -528,9 +528,9 @@ class CRcvBuffer } private: - CUnit** m_pUnit; // Array of pointed units collected in the buffer + srt::CUnit** m_pUnit; // Array of pointed units collected in the buffer const int m_iSize; // Size of the internal array of CUnit* items - CUnitQueue* m_pUnitQueue; // the shared unit queue + srt::CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartPos; // HEAD: first packet available for reading int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index f78689a3a..c7b6e93f3 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -71,6 +71,8 @@ modified by using namespace std; using namespace srt_logging; +namespace srt { + #ifdef _WIN32 // use INVALID_SOCKET, as provided #else @@ -136,17 +138,18 @@ static int set_cloexec(int fd, int set) { #endif // if defined(_AIX) ... #endif // ifndef _WIN32 #endif // if ENABLE_CLOEXEC +} // namespace srt -CChannel::CChannel() +srt::CChannel::CChannel() :m_iSocket(INVALID_SOCKET) { } -CChannel::~CChannel() +srt::CChannel::~CChannel() { } -void CChannel::createSocket(int family) +void srt::CChannel::createSocket(int family) { #if ENABLE_SOCK_CLOEXEC bool cloexec_flag = false; @@ -198,7 +201,7 @@ void CChannel::createSocket(int family) } -void CChannel::open(const sockaddr_any& addr) +void srt::CChannel::open(const sockaddr_any& addr) { createSocket(addr.family()); socklen_t namelen = addr.size(); @@ -212,7 +215,7 @@ void CChannel::open(const sockaddr_any& addr) setUDPSockOpt(); } -void CChannel::open(int family) +void srt::CChannel::open(int family) { createSocket(family); @@ -254,7 +257,7 @@ void CChannel::open(int family) setUDPSockOpt(); } -void CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) +void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) { // The getsockname() call is done before calling it and the // result is placed into udpsocks_addr. @@ -263,7 +266,7 @@ void CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) setUDPSockOpt(); } -void CChannel::setUDPSockOpt() +void srt::CChannel::setUDPSockOpt() { #if defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value @@ -390,7 +393,7 @@ void CChannel::setUDPSockOpt() #endif } -void CChannel::close() const +void srt::CChannel::close() const { #ifndef _WIN32 ::close(m_iSocket); @@ -399,26 +402,26 @@ void CChannel::close() const #endif } -int CChannel::getSndBufSize() +int srt::CChannel::getSndBufSize() { socklen_t size = (socklen_t) sizeof m_mcfg.iUDPSndBufSize; ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*) &m_mcfg.iUDPSndBufSize, &size); return m_mcfg.iUDPSndBufSize; } -int CChannel::getRcvBufSize() +int srt::CChannel::getRcvBufSize() { socklen_t size = (socklen_t) sizeof m_mcfg.iUDPRcvBufSize; ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*) &m_mcfg.iUDPRcvBufSize, &size); return m_mcfg.iUDPRcvBufSize; } -void CChannel::setConfig(const CSrtMuxerConfig& config) +void srt::CChannel::setConfig(const CSrtMuxerConfig& config) { m_mcfg = config; } -int CChannel::getIpTTL() const +int srt::CChannel::getIpTTL() const { if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -441,7 +444,7 @@ int CChannel::getIpTTL() const return m_mcfg.iIpTTL; } -int CChannel::getIpToS() const +int srt::CChannel::getIpToS() const { if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -467,7 +470,7 @@ int CChannel::getIpToS() const } #ifdef SRT_ENABLE_BINDTODEVICE -bool CChannel::getBind(char* dst, size_t len) +bool srt::CChannel::getBind(char* dst, size_t len) { if (m_iSocket == INVALID_SOCKET) return false; // No socket to get data from @@ -485,7 +488,7 @@ bool CChannel::getBind(char* dst, size_t len) } #endif -int CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const +int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; @@ -496,7 +499,7 @@ int CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const return -1; } -int CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const +int srt::CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; @@ -508,7 +511,7 @@ int CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) return -1; } -void CChannel::getSockAddr(sockaddr_any& w_addr) const +void srt::CChannel::getSockAddr(sockaddr_any& w_addr) const { // The getsockname function requires only to have enough target // space to copy the socket name, it doesn't have to be correlated @@ -519,7 +522,7 @@ void CChannel::getSockAddr(sockaddr_any& w_addr) const w_addr.len = namelen; } -void CChannel::getPeerAddr(sockaddr_any& w_addr) const +void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const { socklen_t namelen = (socklen_t) w_addr.storage_size(); ::getpeername(m_iSocket, (w_addr.get()), (&namelen)); @@ -527,7 +530,7 @@ void CChannel::getPeerAddr(sockaddr_any& w_addr) const } -int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const +int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { HLOGC(kslog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID @@ -615,7 +618,7 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const return res; } -EReadStatus CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const +EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const { EReadStatus status = RST_OK; int msg_flags = 0; diff --git a/srtcore/channel.h b/srtcore/channel.h index 6ec51c875..ad6b3f785 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -59,6 +59,8 @@ modified by #include "socketconfig.h" #include "netinet_any.h" +namespace srt { + class CChannel { void createSocket(int family); @@ -114,14 +116,14 @@ class CChannel /// @param [in] packet reference to a CPacket entity. /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, CPacket& packet) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; /// Receive a packet from the channel and record the source address. /// @param [in] addr pointer to the source address. /// @param [in] packet reference to a CPacket entity. /// @return Actual size of data received. - EReadStatus recvfrom(sockaddr_any& addr, CPacket& packet) const; + EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; void setConfig(const CSrtMuxerConfig& config); @@ -160,5 +162,6 @@ class CChannel sockaddr_any m_BindAddr; }; +} // namespace srt #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 31b1bebe0..389933b26 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -70,6 +70,7 @@ modified by #include // SysStrError +using namespace srt; using namespace srt::sync; namespace srt_logging { diff --git a/srtcore/common.h b/srtcore/common.h index 8a47a0c6c..f6fdba44a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -311,7 +311,9 @@ enum EInitEvent TEV_INIT_OHEADBW }; -class CPacket; +namespace srt { + class CPacket; +} // XXX Use some more standard less hand-crafted solution, if possible // XXX Consider creating a mapping between TEV_* values and associated types, @@ -322,7 +324,7 @@ struct EventVariant enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type; union U { - const CPacket* packet; + const srt::CPacket* packet; int32_t ack; struct { @@ -341,7 +343,7 @@ struct EventVariant // Note: UNDEFINED and ARRAY don't have assignment operator. // For ARRAY you'll use 'set' function. For UNDEFINED there's nothing. - explicit EventVariant(const CPacket* arg) + explicit EventVariant(const srt::CPacket* arg) { type = PACKET; u.packet = arg; @@ -430,7 +432,7 @@ class EventArgType; // use a full-templated version. TBD. template<> struct EventVariant::VariantFor { - typedef const CPacket* type; + typedef const srt::CPacket* type; static type U::*field() {return &U::packet;} }; @@ -1406,7 +1408,7 @@ inline std::string SrtVersionString(int version) return buf; } -bool SrtParseConfig(std::string s, SrtConfig& w_config); +bool SrtParseConfig(std::string s, srt::SrtConfig& w_config); struct PacketMetric { diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index fdf9ddb0b..998256216 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -34,6 +34,7 @@ #include "logging.h" using namespace std; +using namespace srt; using namespace srt::sync; using namespace srt_logging; diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 6419605c0..0ad835783 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -16,10 +16,12 @@ #include #include -class CUDT; +namespace srt { + class CUDT; +} class SrtCongestionControlBase; -typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent); +typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent); class SrtCongestion { @@ -97,7 +99,7 @@ class SrtCongestion // in appropriate time. It should select appropriate // congctl basing on the value in selector, then // pin oneself in into CUDT for receiving event signals. - bool configure(CUDT* parent); + bool configure(srt::CUDT* parent); // This function will intentionally delete the contained object. // This makes future calls to ready() return false. Calling @@ -129,13 +131,15 @@ class SrtCongestion }; }; -class CPacket; +namespace srt { + class CPacket; +} class SrtCongestionControlBase { protected: // Here can be some common fields - CUDT* m_parent; + srt::CUDT* m_parent; double m_dPktSndPeriod; double m_dCWndSize; @@ -150,7 +154,7 @@ class SrtCongestionControlBase //char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead. // Constructor in protected section so that this class is semi-abstract. - SrtCongestionControlBase(CUDT* parent); + SrtCongestionControlBase(srt::CUDT* parent); public: // This could be also made abstract, but this causes a linkage @@ -192,7 +196,7 @@ class SrtCongestionControlBase // Arg 2: value calculated out of CUDT's m_config.llInputBW and m_config.iOverheadBW. virtual void updateBandwidth(int64_t, int64_t) {} - virtual bool needsQuickACK(const CPacket&) + virtual bool needsQuickACK(const srt::CPacket&) { return false; } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f6c6cac0a..1e9d2f052 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -83,10 +83,12 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -CUDTUnited CUDT::s_UDTUnited; +namespace srt { + CUDTUnited CUDT::s_UDTUnited; +} -const SRTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; -const int UDT::ERROR = CUDT::ERROR; +const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; +const int UDT::ERROR = srt::CUDT::ERROR; //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -221,7 +223,7 @@ const SrtOptionAction s_sockopt_action; } // namespace srt -void CUDT::construct() +void srt::CUDT::construct() { m_pSndBuffer = NULL; m_pRcvBuffer = NULL; @@ -271,7 +273,7 @@ void CUDT::construct() // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } -CUDT::CUDT(CUDTSocket* parent): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) { construct(); @@ -294,7 +296,7 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent) } -CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) { construct(); @@ -330,7 +332,7 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) m_pCache = ancestor.m_pCache; } -CUDT::~CUDT() +srt::CUDT::~CUDT() { // release mutex/condtion variables destroySynch(); @@ -344,7 +346,7 @@ CUDT::~CUDT() delete m_pRNode; } -void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) +void srt::CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) { if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -403,7 +405,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) } } -void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) +void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) { ScopedLock cg(m_ConnectionLock); @@ -782,7 +784,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) #if ENABLE_EXPERIMENTAL_BONDING -SRT_ERRNO CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) +SRT_ERRNO srt::CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) { SRT_SOCKOPT this_opt = SRTO_VERSION; for (size_t i = 0; i < opt.options.size(); ++i) @@ -796,7 +798,7 @@ SRT_ERRNO CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) } #endif -bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) +bool srt::CUDT::setstreamid(SRTSOCKET u, const std::string &sid) { CUDT *that = getUDTHandle(u); if (!that) @@ -812,7 +814,7 @@ bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) return true; } -std::string CUDT::getstreamid(SRTSOCKET u) +string srt::CUDT::getstreamid(SRTSOCKET u) { CUDT *that = getUDTHandle(u); if (!that) @@ -823,7 +825,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. -void CUDT::clearData() +void srt::CUDT::clearData() { // Initial sequence number, loss, acknowledgement, etc. int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; @@ -912,7 +914,7 @@ void CUDT::clearData() m_tsRcvPeerStartTime = steady_clock::time_point(); } -void CUDT::open() +void srt::CUDT::open() { ScopedLock cg(m_ConnectionLock); @@ -965,7 +967,7 @@ void CUDT::open() m_bOpened = true; } -void CUDT::setListenState() +void srt::CUDT::setListenState() { ScopedLock cg(m_ConnectionLock); @@ -986,7 +988,7 @@ void CUDT::setListenState() m_bListening = true; } -size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) +size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) { if (srtlen < SRT_HS_E_SIZE) { @@ -1014,7 +1016,7 @@ size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, } } -size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) +size_t srt::CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // INITIATOR sends HSREQ. @@ -1077,7 +1079,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu return 3; } -size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) +size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // Setting m_tsRcvPeerStartTime is done in processSrtMsg_HSREQ(), so // this condition will be skipped only if this function is called without @@ -1185,7 +1187,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu return 3; } -size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) +size_t srt::CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) { size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion()); HLOGF(cnlog.Debug, @@ -1201,7 +1203,7 @@ size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) return srtlen; } -void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) +void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) { CPacket srtpkt; int32_t srtcmd = (int32_t)cmd; @@ -1259,7 +1261,7 @@ void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) } } -size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& str) +size_t srt::CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& str) { uint32_t* space = pcmdspec + 1; size_t wordsize = (str.size() + 3) / 4; @@ -1278,7 +1280,7 @@ size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& st #if ENABLE_EXPERIMENTAL_BONDING // [[using locked(m_parent->m_ControlLock)]] // [[using locked(s_UDTUnited.m_GlobControlLock)]] -size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) +size_t srt::CUDT::fillHsExtGroup(uint32_t* pcmdspec) { SRT_ASSERT(m_parent->m_GroupOf != NULL); uint32_t* space = pcmdspec + 1; @@ -1320,7 +1322,7 @@ size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) } #endif -size_t CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) +size_t srt::CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) { uint32_t* space = pcmdspec + 1; @@ -1349,7 +1351,7 @@ size_t CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) return ra_size; } -size_t CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t kmdata_wordsize) +size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t kmdata_wordsize) { uint32_t* space = pcmdspec + 1; const uint32_t failure_kmrsp[] = {SRT_KM_S_UNSECURED}; @@ -1396,7 +1398,7 @@ size_t CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t k // PREREQUISITE: // pkt must be set the buffer and configured for UMSG_HANDSHAKE. // Note that this function replaces also serialization for the HSv4. -bool CUDT::createSrtHandshake( +bool srt::CUDT::createSrtHandshake( int srths_cmd, int srtkm_cmd, const uint32_t* kmdata, @@ -1939,7 +1941,7 @@ RttTracer s_rtt_trace; #endif -bool CUDT::processSrtMsg(const CPacket *ctrlpkt) +bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) { uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); @@ -2021,7 +2023,7 @@ bool CUDT::processSrtMsg(const CPacket *ctrlpkt) return true; } -int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) +int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) { // Set this start time in the beginning, regardless as to whether TSBPD is being // used or not. This must be done in the Initiator as well as Responder. @@ -2235,7 +2237,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t return SRT_CMD_HSRSP; } -int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) +int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) { // XXX Check for mis-version // With HSv4 we accept only version less than 1.3.0 @@ -2381,7 +2383,7 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t } // This function is called only when the URQ_CONCLUSION handshake has been received from the peer. -bool CUDT::interpretSrtHandshake(const CHandShake& hs, +bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, uint32_t* out_data, size_t* pw_len) @@ -2894,7 +2896,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, return true; } -bool CUDT::checkApplyFilterConfig(const std::string &confstr) +bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) { SrtFilterConfig cfg; if (!ParseFilterConfig(confstr, (cfg))) @@ -2976,7 +2978,7 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) } #if ENABLE_EXPERIMENTAL_BONDING -bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UNUSED, int hsreq_type_cmd SRT_ATR_UNUSED) +bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UNUSED, int hsreq_type_cmd SRT_ATR_UNUSED) { // `data_size` isn't checked because we believe it's checked earlier. // Also this code doesn't predict to get any other format than the official one, @@ -3147,7 +3149,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN // exclusively on the listener side (HSD_RESPONDER, HSv5+). // [[using locked(s_UDTUnited.m_GlobControlLock)]] -SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t link_flags) +SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t link_flags) { // Note: This function will lock pg->m_GroupLock! @@ -3245,7 +3247,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l return gp->id(); } -void CUDT::synchronizeWithGroup(CUDTGroup* gp) +void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { ScopedLock gl (*gp->exp_groupLock()); @@ -3344,7 +3346,7 @@ void CUDT::synchronizeWithGroup(CUDTGroup* gp) } #endif -void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) +void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { ScopedLock cg (m_ConnectionLock); @@ -3723,7 +3725,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) } // Asynchronous connection -EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT +EConnectStatus srt::CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT { EConnectStatus cst = CONN_CONTINUE; CUDTException e; @@ -3740,7 +3742,7 @@ EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEP return cst; } -bool CUDT::processAsyncConnectRequest(EReadStatus rst, +bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr) @@ -3834,7 +3836,7 @@ bool CUDT::processAsyncConnectRequest(EReadStatus rst, return status; } -void CUDT::cookieContest() +void srt::CUDT::cookieContest() { if (m_SrtHsSide != HSD_DRAW) return; @@ -3899,7 +3901,7 @@ void CUDT::cookieContest() // - There's no KMX (including first responder's handshake in rendezvous). This writes 0 to w_kmdatasize. // - The encryption status is failure. Respond with fail code and w_kmdatasize = 1. // - The last KMX was successful. Respond with the original kmdata and their size in w_kmdatasize. -EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) +EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) { // If the last CONCLUSION message didn't contain the KMX extension, there's // no key recorded yet, so it can't be extracted. Mark this w_kmdatasize empty though. @@ -3973,7 +3975,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) return CONN_ACCEPT; } -EConnectStatus CUDT::processRendezvous( +EConnectStatus srt::CUDT::processRendezvous( const CPacket& response, const sockaddr_any& serv_addr, EReadStatus rst, CPacket& w_reqpkt) { @@ -4243,7 +4245,7 @@ EConnectStatus CUDT::processRendezvous( } // [[using locked(m_ConnectionLock)]]; -EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTException* eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTException* eout) ATR_NOEXCEPT { // NOTE: ASSUMED LOCK ON: m_ConnectionLock. @@ -4475,7 +4477,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti return postConnect(response, false, eout); } -bool CUDT::applyResponseSettings() ATR_NOEXCEPT +bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT { if (!m_ConnRes.valid()) { @@ -4505,7 +4507,7 @@ bool CUDT::applyResponseSettings() ATR_NOEXCEPT return true; } -EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT { if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. @@ -4713,7 +4715,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE return CONN_ACCEPT; } -void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield) +void srt::CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield) { int enc_flags = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(typefield); @@ -4759,7 +4761,7 @@ void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t ty } // Rendezvous -void CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extension, bool& w_needs_hsrsp) +void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extension, bool& w_needs_hsrsp) { UDTRequestType req = m_ConnRes.m_iReqType; int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); @@ -5123,7 +5125,7 @@ void CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extens * This thread runs only if TsbPd mode is enabled * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay. */ -void *CUDT::tsbpd(void *param) +void * srt::CUDT::tsbpd(void *param) { CUDT *self = (CUDT *)param; @@ -5332,7 +5334,7 @@ void *CUDT::tsbpd(void *param) return NULL; } -void CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) +void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { /* Update drop/skip stats */ enterCS(m_StatsLock); @@ -5347,7 +5349,7 @@ void CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) dropFromLossLists(lastack, CSeqNo::decseq(skiptoseqno)); //remove(from,to-inclusive) } -bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) +bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) { // This will be lazily created due to being the common // code with HSv5 rendezvous, in which this will be run @@ -5409,7 +5411,7 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD return true; } -void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) +void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) { // this is a reponse handshake w_hs.m_iReqType = URQ_CONCLUSION; @@ -5428,7 +5430,7 @@ void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); } -void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, 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 << "acceptAndRespond: setting up data according to handshake"); @@ -5610,7 +5612,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // be created, as this happens before the completion of the connection (and // therefore configuration of the crypter object), which can only take place upon // reception of CONCLUSION response from the listener. -bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) +bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional) { // Lazy initialization if (m_pCryptoControl) @@ -5636,7 +5638,7 @@ bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) return m_pCryptoControl->init(side, bidirectional); } -SRT_REJECT_REASON CUDT::setupCC() +SRT_REJECT_REASON srt::CUDT::setupCC() { // Prepare configuration object, // Create the CCC object and configure it. @@ -5719,7 +5721,7 @@ SRT_REJECT_REASON CUDT::setupCC() return SRT_REJ_UNKNOWN; } -void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) +void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) { // Do a fast pre-check first - this simply declares that agent uses HSv5 // and the legacy SRT Handshake is not to be done. Second check is whether @@ -5769,7 +5771,7 @@ void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) sendSrtMsg(SRT_CMD_HSREQ); } -void CUDT::checkSndTimers(Whether2RegenKm regen) +void srt::CUDT::checkSndTimers(Whether2RegenKm regen) { if (m_SrtHsSide == HSD_INITIATOR) { @@ -5797,7 +5799,7 @@ void CUDT::checkSndTimers(Whether2RegenKm regen) } } -void CUDT::addressAndSend(CPacket& w_pkt) +void srt::CUDT::addressAndSend(CPacket& w_pkt) { w_pkt.m_iID = m_PeerID; setPacketTS(w_pkt, steady_clock::now()); @@ -5811,7 +5813,7 @@ void CUDT::addressAndSend(CPacket& w_pkt) } // [[using maybe_locked(m_GlobControlLock, if called from GC)]] -bool CUDT::closeInternal() +bool srt::CUDT::closeInternal() { // NOTE: this function is called from within the garbage collector thread. @@ -5987,7 +5989,7 @@ bool CUDT::closeInternal() return true; } -int CUDT::receiveBuffer(char *data, int len) +int srt::CUDT::receiveBuffer(char *data, int len) { if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_RECV, data, len, SRT_MSGTTL_INF, false)) throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); @@ -6112,7 +6114,7 @@ int CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -void CUDT::checkNeedDrop(bool& w_bCongestion) +void srt::CUDT::checkNeedDrop(bool& w_bCongestion) { if (!m_bPeerTLPktDrop) return; @@ -6209,7 +6211,7 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) } } -int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) +int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; mctrl.msgttl = msttl; @@ -6221,7 +6223,7 @@ int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t sr // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::send, // which is the only case when the m_parent->m_GroupOf is not NULL. -int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) +int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { bool bCongestion = false; @@ -6508,13 +6510,13 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) return size; } -int CUDT::recv(char* data, int len) +int srt::CUDT::recv(char* data, int len) { SRT_MSGCTRL mctrl = srt_msgctrl_default; return recvmsg2(data, len, (mctrl)); } -int CUDT::recvmsg(char* data, int len, int64_t& srctime) +int srt::CUDT::recvmsg(char* data, int len, int64_t& srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int res = recvmsg2(data, len, (mctrl)); @@ -6525,7 +6527,7 @@ int CUDT::recvmsg(char* data, int len, int64_t& srctime) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::recv, // which is the only case when the m_parent->m_GroupOf is not NULL. -int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) +int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) { // Check if the socket is a member of a receiver group. // If so, then reading by receiveMessage is disallowed. @@ -6557,7 +6559,7 @@ int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) // - 0 - by return value // - 1 - by exception // - 2 - by abort (unused) -int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_exception) +int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_exception) { // Recvmsg isn't restricted to the congctl type, it's the most // basic method of passing the data. You can retrieve data as @@ -6809,7 +6811,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep return res; } -int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) +int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) { if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6931,7 +6933,7 @@ int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) return size - tosend; } -int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) +int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) { if (!m_bConnected || !m_CongCtl.ready()) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); @@ -7050,7 +7052,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) return size - torecv; } -void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) +void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) { if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); @@ -7242,7 +7244,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) } } -bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) +bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) { // Special things that must be done HERE, not in SrtCongestion, // because it involves the input buffer in CUDT. It would be @@ -7360,7 +7362,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) return true; } -void CUDT::initSynch() +void srt::CUDT::initSynch() { setupMutex(m_SendBlockLock, "SendBlock"); setupCond(m_SendBlockCond, "SendBlock"); @@ -7375,7 +7377,7 @@ void CUDT::initSynch() setupCond(m_RcvTsbPdCond, "RcvTsbPd"); } -void CUDT::destroySynch() +void srt::CUDT::destroySynch() { releaseMutex(m_SendBlockLock); @@ -7400,7 +7402,7 @@ void CUDT::destroySynch() releaseCond(m_RcvTsbPdCond); } -void CUDT::releaseSynch() +void srt::CUDT::releaseSynch() { SRT_ASSERT(m_bClosing); // wake up user calls @@ -7430,7 +7432,7 @@ void CUDT::releaseSynch() } // [[using locked(m_RcvBufferLock)]]; -int32_t CUDT::ackDataUpTo(int32_t ack) +int32_t srt::CUDT::ackDataUpTo(int32_t ack) { int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); @@ -7456,6 +7458,7 @@ int32_t CUDT::ackDataUpTo(int32_t ack) return ack; } +namespace srt { #if ENABLE_HEAVY_LOGGING static void DebugAck(string hdr, int prev, int ack) { @@ -7487,8 +7490,9 @@ static void DebugAck(string hdr, int prev, int ack) #else static inline void DebugAck(string, int, int) {} #endif +} -void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size) +void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size) { CPacket ctrlpkt; setPacketTS(ctrlpkt, steady_clock::now()); @@ -7627,7 +7631,7 @@ void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, m_tsLastSndTime = steady_clock::now(); } -int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) +int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); int32_t ack; @@ -7877,7 +7881,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } -void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) +void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) { #if ENABLE_EXPERIMENTAL_BONDING // This is for the call of CSndBuffer::getMsgNoAt that returns @@ -7964,7 +7968,7 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) leaveCS(m_StatsLock); } -void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point& currtime) +void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point& currtime) { const int32_t* ackdata = (const int32_t*)ctrlpkt.m_pcData; const int32_t ackdata_seqno = ackdata[ACKD_RCVLASTACK]; @@ -8216,7 +8220,7 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point leaveCS(m_StatsLock); } -void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) +void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) { int32_t ack = 0; @@ -8307,7 +8311,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival m_iRcvLastAckAck = ack; } -void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) { const int32_t* losslist = (int32_t*)(ctrlpkt.m_pcData); const size_t losslist_len = ctrlpkt.getLength() / 4; @@ -8451,7 +8455,7 @@ void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) leaveCS(m_StatsLock); } -void CUDT::processCtrlHS(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) { CHandShake req; req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); @@ -8562,7 +8566,7 @@ void CUDT::processCtrlHS(const CPacket& ctrlpkt) } } -void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { { const bool using_rexmit_flag = m_bPeerRexmitFlag; @@ -8601,7 +8605,7 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void CUDT::processCtrlShutdown() +void srt::CUDT::processCtrlShutdown() { m_bShutdown = true; m_bClosing = true; @@ -8614,7 +8618,7 @@ void CUDT::processCtrlShutdown() completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! } -void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) { HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) @@ -8644,7 +8648,7 @@ void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) } } -void CUDT::processCtrl(const CPacket &ctrlpkt) +void srt::CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; @@ -8714,7 +8718,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) } } -void CUDT::updateSrtRcvSettings() +void srt::CUDT::updateSrtRcvSettings() { // CHANGED: we need to apply the tsbpd delay only for socket TSBPD. // For Group TSBPD the buffer will have to deliver packets always on request @@ -8745,7 +8749,7 @@ void CUDT::updateSrtRcvSettings() } } -void CUDT::updateSrtSndSettings() +void srt::CUDT::updateSrtSndSettings() { if (m_bPeerTsbPd) { @@ -8768,7 +8772,7 @@ void CUDT::updateSrtSndSettings() } } -void CUDT::updateAfterSrtHandshake(int hsv) +void srt::CUDT::updateAfterSrtHandshake(int hsv) { HLOGC(cnlog.Debug, log << "updateAfterSrtHandshake: HS version " << hsv); // This is blocked from being run in the "app reader" version because here @@ -8823,7 +8827,7 @@ void CUDT::updateAfterSrtHandshake(int hsv) } } -int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime) +int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime) { // protect m_iSndLastDataAck from updating by ACK processing UniqueLock ackguard(m_RecvAckLock); @@ -8935,7 +8939,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime return 0; } -std::pair CUDT::packData(CPacket& w_packet) +std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; bool probe = false; @@ -9216,7 +9220,7 @@ std::pair CUDT::packData(CPacket& w_packet) } // This is a close request, but called from the -void CUDT::processClose() +void srt::CUDT::processClose() { sendCtrl(UMSG_SHUTDOWN); @@ -9242,7 +9246,7 @@ void CUDT::processClose() CGlobEvent::triggerEvent(); } -void CUDT::sendLossReport(const std::vector > &loss_seqs) +void srt::CUDT::sendLossReport(const std::vector > &loss_seqs) { typedef vector > loss_seqs_t; @@ -9274,7 +9278,7 @@ void CUDT::sendLossReport(const std::vector > &loss_ } -bool CUDT::overrideSndSeqNo(int32_t seq) +bool srt::CUDT::overrideSndSeqNo(int32_t seq) { // This function is intended to be called from the socket // group managmenet functions to synchronize the sequnece in @@ -9325,7 +9329,7 @@ bool CUDT::overrideSndSeqNo(int32_t seq) return true; } -int CUDT::processData(CUnit* in_unit) +int srt::CUDT::processData(CUnit* in_unit) { if (m_bClosing) return -1; @@ -9965,7 +9969,7 @@ int CUDT::processData(CUnit* in_unit) } #if ENABLE_EXPERIMENTAL_BONDING -void CUDT::updateIdleLinkFrom(CUDT* source) +void srt::CUDT::updateIdleLinkFrom(CUDT* source) { ScopedLock lg (m_RecvLock); @@ -9999,7 +10003,7 @@ void CUDT::updateIdleLinkFrom(CUDT* source) // XXX This function is currently unused. It should be fixed and put into use. // See the blocked call in CUDT::processData(). // XXX REVIEW LOCKS WHEN REACTIVATING! -CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) +srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) { // [[using affinity(m_pRcvBuffer->workerThread())]]; CUDT* self = (CUDT*)vself; @@ -10074,7 +10078,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) /// do not include the lacking packet. /// The tolerance is not increased infinitely - it's bordered by iMaxReorderTolerance. /// This value can be set in options - SRT_LOSSMAXTTL. -void CUDT::unlose(const CPacket &packet) +void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); int32_t sequence = packet.m_iSeqNo; @@ -10219,7 +10223,7 @@ breakbreak:; } } -void CUDT::dropFromLossLists(int32_t from, int32_t to) +void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) { ScopedLock lg(m_RcvLossLock); m_pRcvLossList->remove(from, to); @@ -10262,7 +10266,7 @@ void CUDT::dropFromLossLists(int32_t from, int32_t to) } // This function, as the name states, should bake a new cookie. -int32_t CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correction) +int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correction) { static unsigned int distractor = 0; unsigned int rollover = distractor + 10; @@ -10321,7 +10325,7 @@ int32_t CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correct // and this will be directly passed to the caller. // [[using locked(m_pRcvQueue->m_LSLock)]]; -int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) +int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] @@ -10675,7 +10679,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) return RejectReasonForURQ(hs.m_iReqType); } -void CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) +void srt::CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) { if (lo == hi) lr.push_back(lo); @@ -10686,7 +10690,7 @@ void CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) } } -int CUDT::checkACKTimer(const steady_clock::time_point &currtime) +int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) { int because_decision = BECAUSE_NO_REASON; if (currtime > m_tsNextACKTime // ACK time has come @@ -10725,7 +10729,7 @@ int CUDT::checkACKTimer(const steady_clock::time_point &currtime) return because_decision; } -int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) +int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) { // XXX The problem with working NAKREPORT with SRT_ARQ_ONREQ // is not that it would be inappropriate, but because it's not @@ -10764,7 +10768,7 @@ int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) return debug_decision; } -bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) +bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) { // VERY HEAVY LOGGING #if ENABLE_HEAVY_LOGGING & 1 @@ -10859,7 +10863,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea return false; } -void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) +void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) { /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. * @@ -10933,7 +10937,7 @@ void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); } -void CUDT::checkTimers() +void srt::CUDT::checkTimers() { // update CC parameters updateCC(TEV_CHECKTIMER, EventVariant(TEV_CHT_INIT)); @@ -10981,7 +10985,7 @@ void CUDT::checkTimers() } } -void CUDT::updateBrokenConnection() +void srt::CUDT::updateBrokenConnection() { m_bClosing = true; releaseSynch(); @@ -10990,7 +10994,7 @@ void CUDT::updateBrokenConnection() CGlobEvent::triggerEvent(); } -void CUDT::completeBrokenConnectionDependencies(int errorcode) +void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) { int token = -1; @@ -11049,7 +11053,7 @@ void CUDT::completeBrokenConnectionDependencies(int errorcode) #endif } -void CUDT::addEPoll(const int eid) +void srt::CUDT::addEPoll(const int eid) { enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.insert(eid); @@ -11071,7 +11075,7 @@ void CUDT::addEPoll(const int eid) } } -void CUDT::removeEPollEvents(const int eid) +void srt::CUDT::removeEPollEvents(const int eid) { // clear IO events notifications; // since this happens after the epoll ID has been removed, they cannot be set again @@ -11080,14 +11084,14 @@ void CUDT::removeEPollEvents(const int eid) s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } -void CUDT::removeEPollID(const int eid) +void srt::CUDT::removeEPollID(const int eid) { enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.erase(eid); leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); } -void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) +void srt::CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) { if (evt >= TEV_E_SIZE) return; // sanity check @@ -11095,7 +11099,7 @@ void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) m_Slots[evt].push_back(sl); } -void CUDT::DisconnectSignal(ETransmissionEvent evt) +void srt::CUDT::DisconnectSignal(ETransmissionEvent evt) { if (evt >= TEV_E_SIZE) return; // sanity check @@ -11103,7 +11107,7 @@ void CUDT::DisconnectSignal(ETransmissionEvent evt) m_Slots[evt].clear(); } -void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) +void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) { for (std::vector::iterator i = m_Slots[tev].begin(); i != m_Slots[tev].end(); ++i) { @@ -11111,7 +11115,7 @@ void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) } } -int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) +int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { CUDTSocket *s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11134,7 +11138,7 @@ int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) return std::abs(timespan); } -int CUDT::rejectReason(SRTSOCKET u) +int srt::CUDT::rejectReason(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11143,7 +11147,7 @@ int CUDT::rejectReason(SRTSOCKET u) return s->m_pUDT->m_RejectReason; } -int CUDT::rejectReason(SRTSOCKET u, int value) +int srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11156,7 +11160,7 @@ int CUDT::rejectReason(SRTSOCKET u, int value) return 0; } -int64_t CUDT::socketStartTime(SRTSOCKET u) +int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11165,7 +11169,7 @@ int64_t CUDT::socketStartTime(SRTSOCKET u) return count_microseconds(s->m_pUDT->m_stats.tsStartTime.time_since_epoch()); } -bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) +bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) { // Prepare the information for the hook. @@ -11266,7 +11270,7 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs return true; } -void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) +void srt::CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) { // Here can be handled some protocol definition // for extra data sent through keepalive. diff --git a/srtcore/core.h b/srtcore/core.h index 1d75bbaea..2e463423c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -137,14 +137,16 @@ enum SeqPairItems SEQ_BEGIN = 0, SEQ_END = 1, SEQ_SIZE = 2 }; -#if ENABLE_EXPERIMENTAL_BONDING -class CUDTGroup; -#endif // Extended SRT Congestion control class - only an incomplete definition required class CCryptoControl; + +namespace srt { class CUDTUnited; class CUDTSocket; +#if ENABLE_EXPERIMENTAL_BONDING +class CUDTGroup; +#endif // XXX REFACTOR: The 'CUDT' class is to be merged with 'CUDTSocket'. // There's no reason for separating them, there's no case of having them @@ -167,10 +169,10 @@ class CUDT friend class PacketFilter; friend class CUDTGroup; friend struct FByOldestActive; // this functional will use private fields - friend class TestMockCUDT; + friend class TestMockCUDT; // unit tests - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; private: // constructor and desctructor void construct(); @@ -1111,12 +1113,12 @@ class CUDT private: // for UDP multiplexer - CSndQueue* m_pSndQueue; // packet sending queue - CRcvQueue* m_pRcvQueue; // packet receiving queue - sockaddr_any m_PeerAddr; // peer address - uint32_t m_piSelfIP[4]; // local UDP IP address - CSNode* m_pSNode; // node information for UDT list used in snd queue - CRNode* m_pRNode; // node information for UDT list used in rcv queue + CSndQueue* m_pSndQueue; // packet sending queue + CRcvQueue* m_pRcvQueue; // packet receiving queue + sockaddr_any m_PeerAddr; // peer address + uint32_t m_piSelfIP[4]; // local UDP IP address + CSNode* m_pSNode; // node information for UDT list used in snd queue + CRNode* m_pRNode; // node information for UDT list used in rcv queue public: // For SrtCongestion const CSndQueue* sndQueue() { return m_pSndQueue; } @@ -1129,5 +1131,6 @@ class CUDT void removeEPollID(const int eid); }; +} // namespace srt #endif diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 5351e1afe..c7c27abab 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -27,6 +27,7 @@ written by #include "logging.h" #include "core.h" +using namespace srt; using namespace srt_logging; #define SRT_MAX_KMRETRY 10 diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 9d1dafc3d..4e067678b 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -38,6 +38,11 @@ extern Logger cnlog; #endif } +namespace srt +{ + class CUDT; +} + // For KMREQ/KMRSP. Only one field is used. const size_t SRT_KMR_KMSTATE = 0; @@ -49,8 +54,7 @@ enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; class CCryptoControl { -//public: - class CUDT* m_parent; + srt::CUDT* m_parent; SRTSOCKET m_SocketID; size_t m_iSndKmKeyLen; //Key length @@ -192,7 +196,7 @@ class CCryptoControl return false; } - CCryptoControl(CUDT* parent, SRTSOCKET id); + CCryptoControl(srt::CUDT* parent, SRTSOCKET id); // DEBUG PURPOSES: std::string CONID() const; @@ -254,14 +258,14 @@ class CCryptoControl /// the encryption will fail. /// XXX Encryption flags in the PH_MSGNO /// field in the header must be correctly set before calling. - EncryptionStatus encrypt(CPacket& w_packet); + EncryptionStatus encrypt(srt::CPacket& w_packet); /// Decrypts the packet. If the packet has ENCKEYSPEC part /// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts /// only if the encryption correctly configured, otherwise it /// fails. After successful decryption, the ENCKEYSPEC part // in PH_MSGNO is set to EK_NOENC. - EncryptionStatus decrypt(CPacket& w_packet); + EncryptionStatus decrypt(srt::CPacket& w_packet); ~CCryptoControl(); }; diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 3786137d5..63533855c 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -348,11 +348,18 @@ std::string DisplayEpollWatch(); } }; +namespace srt +{ + class CUDT; + class CRendezvousQueue; + class CUDTGroup; +} + class CEPoll { -friend class CUDT; -friend class CUDTGroup; -friend class CRendezvousQueue; +friend class srt::CUDT; +friend class srt::CUDTGroup; +friend class srt::CRendezvousQueue; public: CEPoll(); diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index c02af2821..31239eed8 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -35,6 +35,7 @@ using namespace std; using namespace srt_logging; +namespace srt { const char FECFilterBuiltin::defaultConfig [] = "fec,rows:1,layout:staircase,arq:onreq"; @@ -1118,11 +1119,11 @@ static void DebugPrintCells(int32_t base, const std::deque& cells, size_t } // Ok, we have some empty cells, so just adjust to the start of a row. - size_t bstep = i % row_size; - if (i < bstep) // you never know... - i = 0; - else - i -= bstep; + size_t bstep = i % row_size; + if (i < bstep) // you never know... + i = 0; + else + i -= bstep; for ( ; i < cells.size(); i += row_size ) { @@ -2557,3 +2558,5 @@ size_t FECFilterBuiltin::ExtendColumns(size_t colgx) return colgx; } + +} // namespace srt diff --git a/srtcore/fec.h b/srtcore/fec.h index 9423f0d35..57305bfac 100644 --- a/srtcore/fec.h +++ b/srtcore/fec.h @@ -19,6 +19,8 @@ #include "packetfilter_api.h" +namespace srt { + class FECFilterBuiltin: public SrtPacketFilterBase { SrtFilterConfig cfg; @@ -270,4 +272,6 @@ class FECFilterBuiltin: public SrtPacketFilterBase static bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg); }; +} // namespace srt + #endif diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b16bfc349..854508ef4 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -13,6 +13,8 @@ using namespace srt_logging; // The SRT_DEF_VERSION is defined in core.cpp. extern const int32_t SRT_DEF_VERSION; +namespace srt { + int32_t CUDTGroup::s_tokenGen = 0; // [[using locked(this->m_GroupLock)]]; @@ -4587,3 +4589,5 @@ void CUDTGroup::debugGroup() } } #endif + +} // namespace srt diff --git a/srtcore/group.h b/srtcore/group.h index ad779e39f..ec5d124d3 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -22,20 +22,24 @@ Written by #include "group_common.h" #include "group_backup.h" +namespace srt +{ + #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; #endif + class CUDTGroup { friend class CUDTUnited; - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; - typedef srt::sync::steady_clock steady_clock; - typedef srt::groups::SocketData SocketData; - typedef srt::groups::SendBackupCtx SendBackupCtx; - typedef srt::groups::BackupMemberState BackupMemberState; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + typedef sync::steady_clock steady_clock; + typedef groups::SocketData SocketData; + typedef groups::SendBackupCtx SendBackupCtx; + typedef groups::BackupMemberState BackupMemberState; public: typedef SRT_MEMBERSTATUS GroupState; @@ -97,7 +101,7 @@ class CUDTGroup typedef std::list group_t; typedef group_t::iterator gli_t; - typedef std::vector< std::pair > sendable_t; + typedef std::vector< std::pair > sendable_t; struct Sendstate { @@ -211,7 +215,7 @@ class CUDTGroup private: // For Backup, sending all previous packet - int sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc); + int sendBackupRexmit(srt::CUDT& core, SRT_MSGCTRL& w_mc); // Support functions for sendBackup and sendBroadcast /// Check if group member is idle. @@ -232,7 +236,7 @@ class CUDTGroup /// @param[in] currtime current timestamp void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); - void sendBackup_AssignBackupState(CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); + void sendBackup_AssignBackupState(srt::CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); /// Qualify the state of the active link: fresh, stable, unstable, wary. /// @retval active backup member state: fresh, stable, unstable, wary. @@ -319,7 +323,7 @@ class CUDTGroup void setOpt(SRT_SOCKOPT optname, const void* optval, int optlen); void getOpt(SRT_SOCKOPT optName, void* optval, int& w_optlen); - void deriveSettings(CUDT* source); + void deriveSettings(srt::CUDT* source); bool applyFlags(uint32_t flags, HandshakeSide); SRT_SOCKSTATUS getStatus(); @@ -333,14 +337,14 @@ class CUDTGroup return m_type == SRT_GTYPE_BROADCAST; } - srt::sync::Mutex* exp_groupLock() { return &m_GroupLock; } - void addEPoll(int eid); - void removeEPollEvents(const int eid); - void removeEPollID(const int eid); - void updateReadState(SRTSOCKET sock, int32_t sequence); - void updateWriteState(); - void updateFailedLink(); - void activateUpdateEvent(bool still_have_items); + sync::Mutex* exp_groupLock() { return &m_GroupLock; } + void addEPoll(int eid); + void removeEPollEvents(const int eid); + void removeEPollID(const int eid); + void updateReadState(SRTSOCKET sock, int32_t sequence); + void updateWriteState(); + void updateFailedLink(); + void activateUpdateEvent(bool still_have_items); /// Update the in-group array of packet providers per sequence number. /// Also basing on the information already provided by possibly other sockets, @@ -353,16 +357,16 @@ class CUDTGroup /// @param provider The core of the socket for which the packet was dispatched /// @param time TSBPD time of this packet /// @return The bitmap that marks by 'false' packets lost since next to exp_sequence - std::vector providePacket(int32_t exp_sequence, int32_t sequence, CUDT* provider, uint64_t time); + std::vector providePacket(int32_t exp_sequence, int32_t sequence, srt::CUDT* provider, uint64_t time); /// This is called from the ACK action by particular socket, which /// actually signs off the packet for extraction. /// /// @param core The socket core for which the ACK was sent /// @param ack The past-the-last-received ACK sequence number - void readyPackets(CUDT* core, int32_t ack); + void readyPackets(srt::CUDT* core, int32_t ack); - void syncWithSocket(const CUDT& core, const HandshakeSide side); + void syncWithSocket(const srt::CUDT& core, const HandshakeSide side); int getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize); int getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize); int configure(const char* str); @@ -390,7 +394,7 @@ class CUDTGroup // If so, grab the status of all member sockets. void getGroupCount(size_t& w_size, bool& w_still_alive); - class CUDTUnited* m_pGlobal; + class srt::CUDTUnited* m_pGlobal; srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; @@ -493,7 +497,7 @@ class CUDTGroup bool isStillBusy() { - srt::sync::ScopedLock glk(m_GroupLock); + sync::ScopedLock glk(m_GroupLock); return m_iBusy || !m_Group.empty(); } @@ -647,7 +651,7 @@ class CUDTGroup ReadPos* checkPacketAhead(); - void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); + void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); /// The function polls alive member sockets and retrieves a list of read-ready. /// [acquires lock for CUDT::s_UDTUnited.m_GlobControlLock] @@ -656,7 +660,7 @@ class CUDTGroup /// @returns list of read-ready sockets /// @throws CUDTException(MJ_CONNECTION, MN_NOCONN, 0) /// @throws CUDTException(MJ_AGAIN, MN_RDAVAIL, 0) - std::vector recv_WaitForReadReady(const std::vector& aliveMembers, std::set& w_broken); + std::vector recv_WaitForReadReady(const std::vector& aliveMembers, std::set& w_broken); // This is the sequence number of a packet that has been previously // delivered. Initially it should be set to SRT_SEQNO_NONE so that the sequence read @@ -791,11 +795,11 @@ class CUDTGroup } // Live state synchronization - bool getBufferTimeBase(CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); + bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn); - void synchronizeDrift(CUDT* cu, duration udrift, time_point newtimebase); + void synchronizeDrift(srt::CUDT* cu, duration udrift, time_point newtimebase); - void updateLatestRcv(CUDTSocket*); + void updateLatestRcv(srt::CUDTSocket*); // Property accessors SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID); @@ -809,4 +813,6 @@ class CUDTGroup SRTU_PROPERTY_RO(bool, closing, m_bClosing); }; +} // namespace srt + #endif // INC_SRT_GROUP_H diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index 95f9cb899..755fb9dbf 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -58,18 +58,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "utilities.h" using namespace std; - - -CHandShake::CHandShake(): -m_iVersion(0), -m_iType(0), // Universal: UDT_UNDEFINED or no flags -m_iISN(0), -m_iMSS(0), -m_iFlightFlagSize(0), -m_iReqType(URQ_WAVEAHAND), -m_iID(0), -m_iCookie(0), -m_extension(false) +using namespace srt; + + +CHandShake::CHandShake() + : m_iVersion(0) + , m_iType(0) // Universal: UDT_UNDEFINED or no flags + , m_iISN(0) + , m_iMSS(0) + , m_iFlightFlagSize(0) + , m_iReqType(URQ_WAVEAHAND) + , m_iID(0) + , m_iCookie(0) + , m_extension(false) { for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = 0; diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 4524d2232..26a4b2080 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -174,14 +174,14 @@ namespace srt_logging using namespace srt_logging; // Set up the aliases in the constructure -CPacket::CPacket(): -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())) +srt::CPacket::CPacket(): + 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(); @@ -195,12 +195,12 @@ m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) m_PacketVector[PV_DATA].set(NULL, 0); } -char* CPacket::getData() +char* srt::CPacket::getData() { return (char*)m_PacketVector[PV_DATA].dataRef(); } -void CPacket::allocate(size_t alloc_buffer_size) +void srt::CPacket::allocate(size_t alloc_buffer_size) { if (m_data_owned) { @@ -214,14 +214,14 @@ void CPacket::allocate(size_t alloc_buffer_size) m_data_owned = true; } -void CPacket::deallocate() +void srt::CPacket::deallocate() { if (m_data_owned) delete [] (char*)m_PacketVector[PV_DATA].data(); m_PacketVector[PV_DATA].set(NULL, 0); } -char* CPacket::release() +char* srt::CPacket::release() { // When not owned, release returns NULL. char* buffer = NULL; @@ -235,7 +235,7 @@ char* CPacket::release() return buffer; } -CPacket::~CPacket() +srt::CPacket::~CPacket() { // PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer. // Delete the internal buffer only if it was declared as owned. @@ -244,17 +244,17 @@ CPacket::~CPacket() } -size_t CPacket::getLength() const +size_t srt::CPacket::getLength() const { return m_PacketVector[PV_DATA].size(); } -void CPacket::setLength(size_t len) +void srt::CPacket::setLength(size_t len) { m_PacketVector[PV_DATA].setLength(len); } -void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); @@ -365,7 +365,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } } -void CPacket::toNL() +void srt::CPacket::toNL() { // XXX USE HtoNLA! if (isControl()) @@ -383,7 +383,7 @@ void CPacket::toNL() } } -void CPacket::toHL() +void srt::CPacket::toHL() { // convert back into local host order uint32_t* p = m_nHeader; @@ -401,22 +401,22 @@ void CPacket::toHL() } -IOVector* CPacket::getPacketVector() +IOVector* srt::CPacket::getPacketVector() { return m_PacketVector; } -UDTMessageType CPacket::getType() const +UDTMessageType srt::CPacket::getType() const { return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO])); } -int CPacket::getExtendedType() const +int srt::CPacket::getExtendedType() const { return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -int32_t CPacket::getAckSeqNo() const +int32_t srt::CPacket::getAckSeqNo() const { // read additional information field // This field is used only in UMSG_ACK and UMSG_ACKACK, @@ -425,7 +425,7 @@ int32_t CPacket::getAckSeqNo() const return m_nHeader[SRT_PH_MSGNO]; } -uint16_t CPacket::getControlFlags() const +uint16_t srt::CPacket::getControlFlags() const { // This returns exactly the "extended type" value, // which is not used at all in case when the standard @@ -434,17 +434,17 @@ uint16_t CPacket::getControlFlags() const return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -PacketBoundary CPacket::getMsgBoundary() const +PacketBoundary srt::CPacket::getMsgBoundary() const { return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO])); } -bool CPacket::getMsgOrderFlag() const +bool srt::CPacket::getMsgOrderFlag() const { return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]); } -int32_t CPacket::getMsgSeq(bool has_rexmit) const +int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const { if ( has_rexmit ) { @@ -456,13 +456,13 @@ int32_t CPacket::getMsgSeq(bool has_rexmit) const } } -bool CPacket::getRexmitFlag() const +bool srt::CPacket::getRexmitFlag() const { // return false; // return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]); } -EncryptionKeySpec CPacket::getMsgCryptoFlags() const +EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const { return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO])); } @@ -470,19 +470,19 @@ EncryptionKeySpec CPacket::getMsgCryptoFlags() const // This is required as the encryption/decryption happens in place. // This is required to clear off the flags after decryption or set // crypto flags after encrypting a packet. -void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) +void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) { int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask; m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec); } -uint32_t CPacket::getMsgTimeStamp() const +uint32_t srt::CPacket::getMsgTimeStamp() const { // SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK; } -CPacket* CPacket::clone() const +srt::CPacket* srt::CPacket::clone() const { CPacket* pkt = new CPacket; memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE); @@ -493,6 +493,8 @@ CPacket* CPacket::clone() const return pkt; } +namespace srt { + // Useful for debugging std::string PacketMessageFlagStr(uint32_t msgno_field) { @@ -521,8 +523,10 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) os << val; } +} // namespace srt + #if ENABLE_LOGGING -std::string CPacket::Info() +std::string srt::CPacket::Info() { std::ostringstream os; os << "TARGET=@" << m_iID << " "; diff --git a/srtcore/packet.h b/srtcore/packet.h index 8724e9d07..c968b08c0 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -214,9 +214,9 @@ inline EncryptionKeySpec GetEncryptionKeySpec(int32_t msgno) const int32_t PUMASK_SEQNO_PROBE = 0xF; -std::string PacketMessageFlagStr(uint32_t msgno_field); -class CChannel; +namespace srt { +std::string PacketMessageFlagStr(uint32_t msgno_field); class CPacket { @@ -284,7 +284,7 @@ friend class CRcvQueue; void setControl(UDTMessageType type) { - m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); + m_nHeader[srt::SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); } /// Read the extended packet type. @@ -430,4 +430,6 @@ friend class CRcvQueue; #endif }; +} // namespace srt + #endif diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1122c06a2..dffb2a8ba 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace srt_logging; using namespace srt::sync; -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) +bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) { if (!SrtParseConfig(s, (w_config))) return false; @@ -43,13 +43,13 @@ bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config, PacketFilter::F return true; } -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config) +bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config) { return ParseFilterConfig(s, (w_config), NULL); } // Parameters are passed by value because they need to be potentially modicied inside. -bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) +bool srt::CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) { PacketFilter::Factory* fac = PacketFilter::find(w_agent.type); if (!fac) @@ -109,18 +109,20 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) return true; } -struct SortBySequence -{ - bool operator()(const CUnit* u1, const CUnit* u2) +namespace srt { + struct SortBySequence { - int32_t s1 = u1->m_Packet.getSeqNo(); - int32_t s2 = u2->m_Packet.getSeqNo(); + bool operator()(const CUnit* u1, const CUnit* u2) + { + int32_t s1 = u1->m_Packet.getSeqNo(); + int32_t s2 = u2->m_Packet.getSeqNo(); - return CSeqNo::seqcmp(s1, s2) < 0; - } -}; + return CSeqNo::seqcmp(s1, s2) < 0; + } + }; +} // namespace srt -void PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_seqs_t& w_loss_seqs) +void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_seqs_t& w_loss_seqs) { const CPacket& rpkt = unit->m_Packet; @@ -206,7 +208,7 @@ void PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_se } -bool PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) +bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) { bool have = m_filter->packControlPacket(m_sndctlpkt, seq); if (!have) @@ -238,7 +240,7 @@ bool PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) } -void PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) +void srt::PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) { if (m_provided.empty()) return; @@ -273,19 +275,21 @@ void PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) m_provided.clear(); } -bool PacketFilter::IsBuiltin(const string& s) +bool srt::PacketFilter::IsBuiltin(const string& s) { return builtin_filters.count(s); } +namespace srt { std::set PacketFilter::builtin_filters; PacketFilter::filters_map_t PacketFilter::filters; +} -PacketFilter::Factory::~Factory() +srt::PacketFilter::Factory::~Factory() { } -void PacketFilter::globalInit() +void srt::PacketFilter::globalInit() { // Add here builtin packet filters and mark them // as builtin. This will disallow users to register @@ -295,7 +299,7 @@ void PacketFilter::globalInit() builtin_filters.insert("fec"); } -bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr) +bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr) { m_parent = parent; @@ -329,7 +333,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co return true; } -bool PacketFilter::correctConfig(const SrtFilterConfig& conf) +bool srt::PacketFilter::correctConfig(const SrtFilterConfig& conf) { const string* pname = map_getp(conf.parameters, "type"); @@ -346,7 +350,7 @@ bool PacketFilter::correctConfig(const SrtFilterConfig& conf) return true; } -PacketFilter::~PacketFilter() +srt::PacketFilter::~PacketFilter() { delete m_filter; } diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index 545e38e02..a26b07fa5 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -19,6 +19,8 @@ #include "utilities.h" #include "packetfilter_api.h" +namespace srt { + class CUnitQueue; struct CUnit; class CUDT; @@ -212,4 +214,6 @@ inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_fi bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); +} // namespace srt + #endif diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 74279f9e3..d714b865b 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -19,6 +19,8 @@ #include #include +namespace srt { + class CPacket; enum SrtPktHeaderFields @@ -151,6 +153,6 @@ class SrtPacketFilterBase } }; - +} // namespace srt #endif diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index ccc14b9b8..fe323ab4c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -65,7 +65,7 @@ using namespace std; using namespace srt::sync; using namespace srt_logging; -CUnitQueue::CUnitQueue() +srt::CUnitQueue::CUnitQueue() : m_pQEntry(NULL) , m_pCurrQueue(NULL) , m_pLastQueue(NULL) @@ -76,7 +76,7 @@ CUnitQueue::CUnitQueue() { } -CUnitQueue::~CUnitQueue() +srt::CUnitQueue::~CUnitQueue() { CQEntry* p = m_pQEntry; @@ -94,7 +94,7 @@ CUnitQueue::~CUnitQueue() } } -int CUnitQueue::init(int size, int mss, int version) +int srt::CUnitQueue::init(int size, int mss, int version) { CQEntry* tempq = NULL; CUnit* tempu = NULL; @@ -138,7 +138,7 @@ int CUnitQueue::init(int size, int mss, int version) // XXX Lots of common code with CUnitQueue:init. // Consider merging. -int CUnitQueue::increase() +int srt::CUnitQueue::increase() { // adjust/correct m_iCount int real_count = 0; @@ -202,13 +202,13 @@ int CUnitQueue::increase() return 0; } -int CUnitQueue::shrink() +int srt::CUnitQueue::shrink() { // currently queue cannot be shrunk. return -1; } -CUnit* CUnitQueue::getNextAvailUnit() +srt::CUnit* srt::CUnitQueue::getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); @@ -237,7 +237,7 @@ CUnit* CUnitQueue::getNextAvailUnit() return NULL; } -void CUnitQueue::makeUnitFree(CUnit* unit) +void srt::CUnitQueue::makeUnitFree(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); @@ -245,7 +245,7 @@ void CUnitQueue::makeUnitFree(CUnit* unit) --m_iCount; } -void CUnitQueue::makeUnitGood(CUnit* unit) +void srt::CUnitQueue::makeUnitGood(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); @@ -253,7 +253,7 @@ void CUnitQueue::makeUnitGood(CUnit* unit) ++m_iCount; } -CSndUList::CSndUList() +srt::CSndUList::CSndUList() : m_pHeap(NULL) , m_iArrayLength(512) , m_iLastEntry(-1) @@ -265,12 +265,12 @@ CSndUList::CSndUList() m_pHeap = new CSNode*[m_iArrayLength]; } -CSndUList::~CSndUList() +srt::CSndUList::~CSndUList() { delete[] m_pHeap; } -void CSndUList::update(const CUDT* u, EReschedule reschedule) +void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) { ScopedLock listguard(m_ListLock); @@ -296,7 +296,7 @@ void CSndUList::update(const CUDT* u, EReschedule reschedule) insert_(steady_clock::now(), u); } -int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) +int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) { ScopedLock listguard(m_ListLock); @@ -337,14 +337,14 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) return 1; } -void CSndUList::remove(const CUDT* u) +void srt::CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); remove_(u); } -steady_clock::time_point CSndUList::getNextProcTime() +steady_clock::time_point srt::CSndUList::getNextProcTime() { ScopedLock listguard(m_ListLock); @@ -354,7 +354,7 @@ steady_clock::time_point CSndUList::getNextProcTime() return m_pHeap[0]->m_tsTimeStamp; } -void CSndUList::realloc_() +void srt::CSndUList::realloc_() { CSNode** temp = NULL; @@ -373,7 +373,7 @@ void CSndUList::realloc_() m_pHeap = temp; } -void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) +void srt::CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) { // increase the heap array size if necessary if (m_iLastEntry == m_iArrayLength - 1) @@ -382,7 +382,7 @@ void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) insert_norealloc_(ts, u); } -void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) +void srt::CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) { CSNode* n = u->m_pSNode; @@ -422,7 +422,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT } } -void CSndUList::remove_(const CUDT* u) +void srt::CSndUList::remove_(const CUDT* u) { CSNode* n = u->m_pSNode; @@ -462,7 +462,7 @@ void CSndUList::remove_(const CUDT* u) } // -CSndQueue::CSndQueue() +srt::CSndQueue::CSndQueue() : m_pSndUList(NULL) , m_pChannel(NULL) , m_pTimer(NULL) @@ -472,7 +472,7 @@ CSndQueue::CSndQueue() setupCond(m_WindowCond, "Window"); } -CSndQueue::~CSndQueue() +srt::CSndQueue::~CSndQueue() { m_bClosing = true; @@ -493,20 +493,20 @@ CSndQueue::~CSndQueue() delete m_pSndUList; } -int CSndQueue::ioctlQuery(int type) const +int srt::CSndQueue::ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } -int CSndQueue::sockoptQuery(int level, int type) const +int srt::CSndQueue::sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } #if ENABLE_LOGGING -int CSndQueue::m_counter = 0; +int srt::CSndQueue::m_counter = 0; #endif -void CSndQueue::init(CChannel* c, CTimer* t) +void srt::CSndQueue::init(CChannel* c, CTimer* t) { m_pChannel = c; m_pTimer = t; @@ -526,24 +526,24 @@ void CSndQueue::init(CChannel* c, CTimer* t) throw CUDTException(MJ_SYSTEMRES, MN_THREAD); } -int CSndQueue::getIpTTL() const +int srt::CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : -1; } -int CSndQueue::getIpToS() const +int srt::CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } #ifdef SRT_ENABLE_BINDTODEVICE -bool CSndQueue::getBind(char* dst, size_t len) const +bool srt::CSndQueue::getBind(char* dst, size_t len) const { return m_pChannel ? m_pChannel->getBind(dst, len) : false; } #endif -void* CSndQueue::worker(void* param) +void* srt::CSndQueue::worker(void* param) { CSndQueue* self = (CSndQueue*)param; @@ -645,7 +645,7 @@ void* CSndQueue::worker(void* param) return NULL; } -int CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) +int srt::CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) { // send out the packet immediately (high priority), this is a control packet m_pChannel->sendto(w_addr, w_packet); @@ -653,15 +653,15 @@ int CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) } // -CRcvUList::CRcvUList() +srt::CRcvUList::CRcvUList() : m_pUList(NULL) , m_pLast(NULL) { } -CRcvUList::~CRcvUList() {} +srt::CRcvUList::~CRcvUList() {} -void CRcvUList::insert(const CUDT* u) +void srt::CRcvUList::insert(const CUDT* u) { CRNode* n = u->m_pRNode; n->m_tsTimeStamp = steady_clock::now(); @@ -682,7 +682,7 @@ void CRcvUList::insert(const CUDT* u) m_pLast = n; } -void CRcvUList::remove(const CUDT* u) +void srt::CRcvUList::remove(const CUDT* u) { CRNode* n = u->m_pRNode; @@ -713,7 +713,7 @@ void CRcvUList::remove(const CUDT* u) n->m_pNext = n->m_pPrev = NULL; } -void CRcvUList::update(const CUDT* u) +void srt::CRcvUList::update(const CUDT* u) { CRNode* n = u->m_pRNode; @@ -744,13 +744,13 @@ void CRcvUList::update(const CUDT* u) } // -CHash::CHash() +srt::CHash::CHash() : m_pBucket(NULL) , m_iHashSize(0) { } -CHash::~CHash() +srt::CHash::~CHash() { for (int i = 0; i < m_iHashSize; ++i) { @@ -766,7 +766,7 @@ CHash::~CHash() delete[] m_pBucket; } -void CHash::init(int size) +void srt::CHash::init(int size) { m_pBucket = new CBucket*[size]; @@ -776,7 +776,7 @@ void CHash::init(int size) m_iHashSize = size; } -CUDT* CHash::lookup(int32_t id) +srt::CUDT* srt::CHash::lookup(int32_t id) { // simple hash function (% hash table size); suitable for socket descriptors CBucket* b = m_pBucket[id % m_iHashSize]; @@ -791,7 +791,7 @@ CUDT* CHash::lookup(int32_t id) return NULL; } -void CHash::insert(int32_t id, CUDT* u) +void srt::CHash::insert(int32_t id, CUDT* u) { CBucket* b = m_pBucket[id % m_iHashSize]; @@ -803,7 +803,7 @@ void CHash::insert(int32_t id, CUDT* u) m_pBucket[id % m_iHashSize] = n; } -void CHash::remove(int32_t id) +void srt::CHash::remove(int32_t id) { CBucket* b = m_pBucket[id % m_iHashSize]; CBucket* p = NULL; @@ -828,18 +828,18 @@ void CHash::remove(int32_t id) } // -CRendezvousQueue::CRendezvousQueue() +srt::CRendezvousQueue::CRendezvousQueue() : m_lRendezvousID() , m_RIDListLock() { } -CRendezvousQueue::~CRendezvousQueue() +srt::CRendezvousQueue::~CRendezvousQueue() { m_lRendezvousID.clear(); } -void CRendezvousQueue::insert(const SRTSOCKET& id, +void srt::CRendezvousQueue::insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) @@ -858,7 +858,7 @@ void CRendezvousQueue::insert(const SRTSOCKET& id, << " (total connectors: " << m_lRendezvousID.size() << ")"); } -void CRendezvousQueue::remove(const SRTSOCKET& id) +void srt::CRendezvousQueue::remove(const SRTSOCKET& id) { ScopedLock lkv(m_RIDListLock); @@ -872,7 +872,7 @@ void CRendezvousQueue::remove(const SRTSOCKET& id) } } -CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const +srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { ScopedLock vg(m_RIDListLock); @@ -903,7 +903,7 @@ CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) cons return NULL; } -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) +void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) { vector toRemove, toProcess; @@ -1005,7 +1005,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con } } -bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, +bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, int iDstSockID, vector& toRemove, @@ -1109,7 +1109,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, } // -CRcvQueue::CRcvQueue() +srt::CRcvQueue::CRcvQueue() : m_WorkerThread() , m_UnitQueue() , m_pRcvUList(NULL) @@ -1129,7 +1129,7 @@ CRcvQueue::CRcvQueue() setupCond(m_BufferCond, "QueueBuffer"); } -CRcvQueue::~CRcvQueue() +srt::CRcvQueue::~CRcvQueue() { m_bClosing = true; @@ -1158,10 +1158,10 @@ CRcvQueue::~CRcvQueue() } #if ENABLE_LOGGING -int CRcvQueue::m_counter = 0; +int srt::CRcvQueue::m_counter = 0; #endif -void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) +void srt::CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) { m_szPayloadSize = payload; @@ -1189,7 +1189,7 @@ void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel } } -void* CRcvQueue::worker(void* param) +void* srt::CRcvQueue::worker(void* param) { CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->m_UnitQueue.getIPversion()); @@ -1322,7 +1322,7 @@ void* CRcvQueue::worker(void* param) return NULL; } -EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) +EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) { #if !USE_BUSY_WAITING // This might be not really necessary, and probably @@ -1380,7 +1380,7 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad return rst; } -EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) { HLOGC(cnlog.Debug, log << "Got sockID=0 from " << addr.str() << " - trying to resolve it as a connection request..."); @@ -1423,7 +1423,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. } -EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); if (!u) @@ -1475,7 +1475,7 @@ EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, // This function then tries to manage the packet as a rendezvous connection // request in ASYNC mode; when this is not applicable, it stores the packet // in the "receiving queue" so that it will be picked up in the "main" thread. -EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) { // This 'retrieve' requires that 'id' be either one of those // stored in the rendezvous queue (see CRcvQueue::registerConnector) @@ -1605,7 +1605,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c return CONN_CONTINUE; } -void CRcvQueue::stopWorker() +void srt::CRcvQueue::stopWorker() { // We use the decent way, so we say to the thread "please exit". m_bClosing = true; @@ -1622,7 +1622,7 @@ void CRcvQueue::stopWorker() m_WorkerThread.join(); } -int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) +int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) { UniqueLock bufferlock(m_BufferLock); CSync buffercond(m_BufferCond, bufferlock); @@ -1676,7 +1676,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) return (int)w_packet.getLength(); } -int CRcvQueue::setListener(CUDT* u) +int srt::CRcvQueue::setListener(CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1687,7 +1687,7 @@ int CRcvQueue::setListener(CUDT* u) return 0; } -void CRcvQueue::removeListener(const CUDT* u) +void srt::CRcvQueue::removeListener(const CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1695,7 +1695,7 @@ void CRcvQueue::removeListener(const CUDT* u) m_pListener = NULL; } -void CRcvQueue::registerConnector(const SRTSOCKET& id, +void srt::CRcvQueue::registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) @@ -1705,7 +1705,7 @@ void CRcvQueue::registerConnector(const SRTSOCKET& id, m_pRendezvousQueue->insert(id, u, addr, ttl); } -void CRcvQueue::removeConnector(const SRTSOCKET& id) +void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) { HLOGC(cnlog.Debug, log << "removeConnector: removing @" << id); m_pRendezvousQueue->remove(id); @@ -1727,19 +1727,19 @@ void CRcvQueue::removeConnector(const SRTSOCKET& id) } } -void CRcvQueue::setNewEntry(CUDT* u) +void srt::CRcvQueue::setNewEntry(CUDT* u) { HLOGC(cnlog.Debug, log << CUDTUnited::CONID(u->m_SocketID) << "setting socket PENDING FOR CONNECTION"); ScopedLock listguard(m_IDLock); m_vNewEntry.push_back(u); } -bool CRcvQueue::ifNewEntry() +bool srt::CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } -CUDT* CRcvQueue::getNewEntry() +srt::CUDT* srt::CRcvQueue::getNewEntry() { ScopedLock listguard(m_IDLock); @@ -1752,7 +1752,7 @@ CUDT* CRcvQueue::getNewEntry() return u; } -void CRcvQueue::storePkt(int32_t id, CPacket* pkt) +void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) { UniqueLock bufferlock(m_BufferLock); CSync passcond(m_BufferCond, bufferlock); @@ -1774,7 +1774,7 @@ void CRcvQueue::storePkt(int32_t id, CPacket* pkt) } } -void CMultiplexer::destroy() +void srt::CMultiplexer::destroy() { // Reverse order of the assigned delete m_pRcvQueue; diff --git a/srtcore/queue.h b/srtcore/queue.h index a412fd516..b2ccb36d8 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -63,8 +63,10 @@ modified by #include #include -class CUDT; +namespace srt +{ class CChannel; +class CUDT; struct CUnit { @@ -92,7 +94,6 @@ class CUnitQueue /// @param [in] mss maximum segment size /// @param [in] version IP version /// @return 0: success, -1: failure. - int init(int size, int mss, int version); /// Increase (double) the unit queue size. @@ -112,7 +113,6 @@ class CUnitQueue public: // Operations on units /// find an available unit for incoming packet. /// @return Pointer to the available unit, NULL if not found. - CUnit* getNextAvailUnit(); void makeUnitFree(CUnit* unit); @@ -149,8 +149,8 @@ class CUnitQueue struct CSNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + sync::steady_clock::time_point m_tsTimeStamp; int m_iHeapLoc; // location on the heap, -1 means not on the heap }; @@ -193,7 +193,7 @@ class CSndUList /// Retrieve the next scheduled processing time. /// @return Scheduled processing time of the first UDT socket in the list. - srt::sync::steady_clock::time_point getNextProcTime(); + sync::steady_clock::time_point getNextProcTime(); private: /// Doubles the size of the list. @@ -204,14 +204,14 @@ class CSndUList /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); + void insert_(const sync::steady_clock::time_point& ts, const CUDT* u); /// Insert a new UDT instance into the list without realloc. /// Should be called if there is a gauranteed space for the element. /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); + void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u); void remove_(const CUDT* u); @@ -220,12 +220,12 @@ class CSndUList int m_iArrayLength; // physical length of the array int m_iLastEntry; // position of last entry on the heap array - srt::sync::Mutex m_ListLock; + sync::Mutex m_ListLock; - srt::sync::Mutex* m_pWindowLock; - srt::sync::Condition* m_pWindowCond; + sync::Mutex* m_pWindowLock; + sync::Condition* m_pWindowCond; - srt::sync::CTimer* m_pTimer; + sync::CTimer* m_pTimer; private: CSndUList(const CSndUList&); @@ -234,8 +234,8 @@ class CSndUList struct CRNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp CRNode* m_pPrev; // previous link CRNode* m_pNext; // next link @@ -401,7 +401,7 @@ class CRendezvousQueue }; std::list m_lRendezvousID; // The sockets currently in rendezvous mode - mutable srt::sync::Mutex m_RIDListLock; + mutable sync::Mutex m_RIDListLock; }; class CSndQueue @@ -514,7 +514,7 @@ class CRcvQueue /// @param [in] c UDP channel to be associated to the queue /// @param [in] t timer - void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); + void init(int size, size_t payload, int version, int hsize, CChannel* c, sync::CTimer* t); /// Read a packet for a specific UDT socket id. /// @param [in] id Socket ID @@ -528,8 +528,8 @@ class CRcvQueue void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; + static void* worker(void* param); + sync::CThread m_WorkerThread; // Subroutines of worker EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); @@ -537,11 +537,11 @@ class CRcvQueue EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); private: - CUnitQueue m_UnitQueue; // The received packet queue - CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue - CHash* m_pHash; // Hash table for UDT socket looking up - CChannel* m_pChannel; // UDP channel for receving packets - srt::sync::CTimer* m_pTimer; // shared timer with the snd queue + CUnitQueue m_UnitQueue; // The received packet queue + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + sync::CTimer* m_pTimer; // shared timer with the snd queue size_t m_szPayloadSize; // packet payload size @@ -554,10 +554,10 @@ class CRcvQueue int setListener(CUDT* u); void removeListener(const CUDT* u); - void registerConnector(const SRTSOCKET& id, - CUDT* u, - const sockaddr_any& addr, - const srt::sync::steady_clock::time_point& ttl); + void registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const sync::steady_clock::time_point& ttl); void removeConnector(const SRTSOCKET& id); void setNewEntry(CUDT* u); @@ -567,16 +567,16 @@ class CRcvQueue void storePkt(int32_t id, CPacket* pkt); private: - srt::sync::Mutex m_LSLock; + sync::Mutex m_LSLock; CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode std::vector m_vNewEntry; // newly added entries, to be inserted - srt::sync::Mutex m_IDLock; + sync::Mutex m_IDLock; std::map > m_mBuffer; // temporary buffer for rendezvous connection request - srt::sync::Mutex m_BufferLock; - srt::sync::Condition m_BufferCond; + sync::Mutex m_BufferLock; + sync::Condition m_BufferCond; private: CRcvQueue(const CRcvQueue&); @@ -585,10 +585,10 @@ class CRcvQueue struct CMultiplexer { - CSndQueue* m_pSndQueue; // The sending queue - CRcvQueue* m_pRcvQueue; // The receiving queue - CChannel* m_pChannel; // The UDP channel for sending and receiving - srt::sync::CTimer* m_pTimer; // The timer + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + sync::CTimer* m_pTimer; // The timer int m_iPort; // The UDP port number of this multiplexer int m_iIPversion; // Address family (AF_INET or AF_INET6) @@ -611,4 +611,6 @@ struct CMultiplexer void destroy(); }; +} // namespace srt + #endif diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 1db4bb63a..72b90195e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -396,7 +396,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iMSS = ival; @@ -435,7 +435,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::UDP_HDR_SIZE); + co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); } }; @@ -449,7 +449,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; + const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; @@ -937,8 +937,8 @@ struct CSrtConfigSetter // This means that the filter might have been installed before, // and the fix to the maximum payload size was already applied. // This needs to be checked now. - SrtFilterConfig fc; - if (!ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) + srt::SrtFilterConfig fc; + if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) { // Break silently. This should not happen LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); @@ -1128,9 +1128,9 @@ struct CSrtConfigSetter using namespace srt_logging; std::string arg((const char*)optval, optlen); // Parse the configuration string prematurely - SrtFilterConfig fc; - PacketFilter::Factory* fax = 0; - if (!ParseFilterConfig(arg, (fc), (&fax))) + srt::SrtFilterConfig fc; + srt::PacketFilter::Factory* fax = 0; + if (!srt::ParseFilterConfig(arg, (fc), (&fax))) { LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index bfd130148..dbe668743 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -24,6 +24,7 @@ written by #include "utilities.h" using namespace std; +using namespace srt; extern "C" { diff --git a/srtcore/udt.h b/srtcore/udt.h index 8d7d5eb87..b1af441b2 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -249,7 +249,7 @@ namespace logging { using namespace srt_logging; } -} +} // namespace srt // Planned deprecated removal: rel1.6.0 // There's also no portable way possible to enforce a deprecation diff --git a/srtcore/window.cpp b/srtcore/window.cpp index ea8386fe1..96562d7a4 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -151,7 +151,7 @@ void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeW r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec for (size_t i = 0; i < asize; ++ i) - r_bytesWindow[i] = CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] + r_bytesWindow[i] = srt::CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] } @@ -188,7 +188,7 @@ int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, cons // claculate speed, or return 0 if not enough valid value if (count > (asize >> 1)) { - bytes += (CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received + bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes))); return (int)ceil(1000000.0 / (sum / count)); } diff --git a/srtcore/window.h b/srtcore/window.h index 7dbfb73bf..d1065b3da 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -231,7 +231,7 @@ class CPktTimeWindow: CPktTimeWindowTools } /// Shortcut to test a packet for possible probe 1 or 2 - void probeArrival(const CPacket& pkt, bool unordered) + void probeArrival(const srt::CPacket& pkt, bool unordered) { const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; @@ -252,7 +252,7 @@ class CPktTimeWindow: CPktTimeWindowTools } /// Record the arrival time of the first probing packet. - void probe1Arrival(const CPacket& pkt, bool unordered) + void probe1Arrival(const srt::CPacket& pkt, bool unordered) { if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) { @@ -269,7 +269,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the second probing packet and the interval between packet pairs. - void probe2Arrival(const CPacket& pkt) + void probe2Arrival(const srt::CPacket& pkt) { // Reject probes that don't refer to the very next packet // towards the one that was lately notified by probe1Arrival. @@ -299,7 +299,7 @@ class CPktTimeWindow: CPktTimeWindowTools // record the probing packets interval // Adjust the time for what a complete packet would have take const int64_t timediff = srt::sync::count_microseconds(m_tsCurrArrTime - m_tsProbeTime); - const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE; + const int64_t timediff_times_pl_size = timediff * srt::CPacket::SRT_MAX_PAYLOAD_SIZE; // Let's take it simpler than it is coded here: // (stating that a packet has never zero size) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 795a6d52f..047ced7e2 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -2,6 +2,8 @@ #include "gtest/gtest.h" #include "buffer.h" +using namespace srt; + TEST(CRcvBuffer, Create) { diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 913473af7..8ea7ea26b 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -13,6 +13,7 @@ #include "api.h" using namespace std; +using namespace srt; class TestFECRebuilding: public testing::Test { @@ -91,16 +92,18 @@ class TestFECRebuilding: public testing::Test } }; -class TestMockCUDT -{ -public: - CUDT* core; - - bool checkApplyFilterConfig(const string& s) +namespace srt { + class TestMockCUDT { - return core->checkApplyFilterConfig(s); - } -}; + public: + CUDT* core; + + bool checkApplyFilterConfig(const string& s) + { + return core->checkApplyFilterConfig(s); + } + }; +} // The expected whole procedure of connection using FEC is // expected to: diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index df2ecca53..4a1cb0ec8 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -2,6 +2,8 @@ #include "common.h" #include "core.h" +using namespace srt; + const int32_t CSeqNo::m_iSeqNoTH; const int32_t CSeqNo::m_iMaxSeqNo; diff --git a/test/test_unitqueue.cpp b/test/test_unitqueue.cpp index f9010b9e7..324766f39 100644 --- a/test/test_unitqueue.cpp +++ b/test/test_unitqueue.cpp @@ -3,9 +3,8 @@ #include "gtest/gtest.h" #include "queue.h" - using namespace std; - +using namespace srt; /// Create CUnitQueue with queue size of 4 units. /// The size of 4 is chosen on purpose, because diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 1cb49cb64..501e5f8d7 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -2903,7 +2903,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; - if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) Error(SysError(), "Setting timeout for UDP"); } From 22cc92424acd650ee0238eb092df521e19434068 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 12:15:08 +0200 Subject: [PATCH 186/790] [core] Applied clang-format on channel.h and cpp --- srtcore/channel.cpp | 574 ++++++++++++++++++++++---------------------- srtcore/channel.h | 128 +++++----- 2 files changed, 356 insertions(+), 346 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index c7b6e93f3..6e56e97f7 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -53,7 +53,7 @@ modified by #include "platform_sys.h" #include -#include // Logging +#include // Logging #include #include @@ -65,34 +65,30 @@ modified by #include "utilities.h" #ifdef _WIN32 - typedef int socklen_t; +typedef int socklen_t; #endif using namespace std; using namespace srt_logging; -namespace srt { +namespace srt +{ #ifdef _WIN32 - // use INVALID_SOCKET, as provided +// use INVALID_SOCKET, as provided #else - static const int INVALID_SOCKET = -1; +static const int INVALID_SOCKET = -1; #endif #if ENABLE_SOCK_CLOEXEC #ifndef _WIN32 -#if defined(_AIX) || \ - defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__linux__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) +#if defined(_AIX) || defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) // Set the CLOEXEC flag using ioctl() function -static int set_cloexec(int fd, int set) { +static int set_cloexec(int fd, int set) +{ int r; do @@ -106,7 +102,8 @@ static int set_cloexec(int fd, int set) { } #else // Set the CLOEXEC flag using fcntl() function -static int set_cloexec(int fd, int set) { +static int set_cloexec(int fd, int set) +{ int flags; int r; @@ -141,13 +138,11 @@ static int set_cloexec(int fd, int set) { } // namespace srt srt::CChannel::CChannel() - :m_iSocket(INVALID_SOCKET) + : m_iSocket(INVALID_SOCKET) { } -srt::CChannel::~CChannel() -{ -} +srt::CChannel::~CChannel() {} void srt::CChannel::createSocket(int family) { @@ -158,14 +153,14 @@ void srt::CChannel::createSocket(int family) m_iSocket = ::socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); if (m_iSocket == INVALID_SOCKET) { - m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); + m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; } #else - m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); + m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; #endif -#else // ENABLE_SOCK_CLOEXEC +#else // ENABLE_SOCK_CLOEXEC m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); #endif // ENABLE_SOCK_CLOEXEC @@ -174,10 +169,12 @@ void srt::CChannel::createSocket(int family) #if ENABLE_SOCK_CLOEXEC #ifdef _WIN32 - // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) + // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) #else - if (cloexec_flag) { - if (0 != set_cloexec(m_iSocket, 1)) { + if (cloexec_flag) + { + if (0 != set_cloexec(m_iSocket, 1)) + { throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); } } @@ -186,19 +183,19 @@ void srt::CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - const int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, - (const char*) &m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); + const int res ATR_UNUSED = + ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); #if ENABLE_LOGGING if (res == -1) { - int err = errno; + int err = errno; char msg[160]; - LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " - << m_mcfg.iIpV6Only << ": " << SysStrError(err, msg, 159)); + LOGC(kmlog.Error, + log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " << m_mcfg.iIpV6Only << ": " + << SysStrError(err, msg, 159)); } #endif // ENABLE_LOGGING } - } void srt::CChannel::open(const sockaddr_any& addr) @@ -219,14 +216,14 @@ void srt::CChannel::open(int family) { createSocket(family); - //sendto or WSASendTo will also automatically bind the socket - addrinfo hints; + // sendto or WSASendTo will also automatically bind the socket + addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; const int eai = ::getaddrinfo(NULL, "0", &hints, &res); @@ -248,7 +245,7 @@ void srt::CChannel::open(int family) ::freeaddrinfo(res); throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } - m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t) res->ai_addrlen); + m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t)res->ai_addrlen); ::freeaddrinfo(res); @@ -261,159 +258,169 @@ void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) { // The getsockname() call is done before calling it and the // result is placed into udpsocks_addr. - m_iSocket = udpsock; + m_iSocket = udpsock; m_BindAddr = udpsocks_addr; setUDPSockOpt(); } void srt::CChannel::setUDPSockOpt() { - #if defined(BSD) || TARGET_OS_MAC - // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value - int maxsize = 64000; - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &maxsize, sizeof maxsize); - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &maxsize, sizeof maxsize); - #else - // for other systems, if requested is greated than maximum, the maximum value will be automactally used - if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) || - (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - #endif - - if (m_mcfg.iIpTTL != -1) - { - if (m_BindAddr.family() == AF_INET) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - else - { - // If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS. - - // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - } - } - - if (m_mcfg.iIpToS != -1) - { - if (m_BindAddr.family() == AF_INET) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - else - { - // If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS. +#if defined(BSD) || TARGET_OS_MAC + // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value + int maxsize = 64000; + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&maxsize, sizeof maxsize); + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&maxsize, sizeof maxsize); +#else + // for other systems, if requested is greated than maximum, the maximum value will be automactally used + if ((0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) || + (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); +#endif + + if (m_mcfg.iIpTTL != -1) + { + if (m_BindAddr.family() == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS. + + // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || + !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != + ::setsockopt( + m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } + + if (m_mcfg.iIpToS != -1) + { + if (m_BindAddr.family() == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS. #ifdef IPV6_TCLASS - // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } + // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || + !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt( + m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } #endif - // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - } - } + // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } #ifdef SRT_ENABLE_BINDTODEVICE - if (!m_mcfg.sBindToDevice.empty()) - { - if (m_BindAddr.family() != AF_INET) - { - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE can only be set with AF_INET connections"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, - m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) - { + if (!m_mcfg.sBindToDevice.empty()) + { + if (m_BindAddr.family() != AF_INET) + { + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE can only be set with AF_INET connections"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) + { #if ENABLE_LOGGING - char buf[255]; - const char* err = SysStrError(NET_ERROR, buf, 255); - LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); + char buf[255]; + const char* err = SysStrError(NET_ERROR, buf, 255); + LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); #endif // ENABLE_LOGGING - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } #endif #ifdef UNIX - // Set non-blocking I/O - // UNIX does not support SO_RCVTIMEO - int opts = ::fcntl(m_iSocket, F_GETFL); - if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + // Set non-blocking I/O + // UNIX does not support SO_RCVTIMEO + int opts = ::fcntl(m_iSocket, F_GETFL); + if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #elif defined(_WIN32) - u_long nonBlocking = 1; - if (0 != ioctlsocket (m_iSocket, FIONBIO, &nonBlocking)) - throw CUDTException (MJ_SETUP, MN_NORES, NET_ERROR); + u_long nonBlocking = 1; + if (0 != ioctlsocket(m_iSocket, FIONBIO, &nonBlocking)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #else - timeval tv; - tv.tv_sec = 0; -#if defined (BSD) || TARGET_OS_MAC - // Known BSD bug as the day I wrote this code. - // A small time out value will cause the socket to block forever. - tv.tv_usec = 10000; + timeval tv; + tv.tv_sec = 0; +#if defined(BSD) || TARGET_OS_MAC + // Known BSD bug as the day I wrote this code. + // A small time out value will cause the socket to block forever. + tv.tv_usec = 10000; #else - tv.tv_usec = 100; + tv.tv_usec = 100; #endif - // Set receiving time-out value - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + // Set receiving time-out value + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #endif } void srt::CChannel::close() const { - #ifndef _WIN32 - ::close(m_iSocket); - #else - ::closesocket(m_iSocket); - #endif +#ifndef _WIN32 + ::close(m_iSocket); +#else + ::closesocket(m_iSocket); +#endif } int srt::CChannel::getSndBufSize() { - socklen_t size = (socklen_t) sizeof m_mcfg.iUDPSndBufSize; - ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*) &m_mcfg.iUDPSndBufSize, &size); - return m_mcfg.iUDPSndBufSize; + socklen_t size = (socklen_t)sizeof m_mcfg.iUDPSndBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_mcfg.iUDPSndBufSize, &size); + return m_mcfg.iUDPSndBufSize; } int srt::CChannel::getRcvBufSize() { - socklen_t size = (socklen_t) sizeof m_mcfg.iUDPRcvBufSize; - ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*) &m_mcfg.iUDPRcvBufSize, &size); - return m_mcfg.iUDPRcvBufSize; + socklen_t size = (socklen_t)sizeof m_mcfg.iUDPRcvBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_mcfg.iUDPRcvBufSize, &size); + return m_mcfg.iUDPRcvBufSize; } void srt::CChannel::setConfig(const CSrtMuxerConfig& config) @@ -423,50 +430,50 @@ void srt::CChannel::setConfig(const CSrtMuxerConfig& config) int srt::CChannel::getIpTTL() const { - if (m_iSocket == INVALID_SOCKET) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - socklen_t size = (socklen_t) sizeof m_mcfg.iIpTTL; - if (m_BindAddr.family() == AF_INET) - { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char*) &m_mcfg.iIpTTL, &size); - } - else if (m_BindAddr.family() == AF_INET6) - { - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char*) &m_mcfg.iIpTTL, &size); - } - else - { - // If family is unspecified, the socket probably doesn't exist. - LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - return m_mcfg.iIpTTL; + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + socklen_t size = (socklen_t)sizeof m_mcfg.iIpTTL; + if (m_BindAddr.family() == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char*)&m_mcfg.iIpTTL, &size); + } + else if (m_BindAddr.family() == AF_INET6) + { + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char*)&m_mcfg.iIpTTL, &size); + } + else + { + // If family is unspecified, the socket probably doesn't exist. + LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + return m_mcfg.iIpTTL; } int srt::CChannel::getIpToS() const { - if (m_iSocket == INVALID_SOCKET) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - socklen_t size = (socklen_t) sizeof m_mcfg.iIpToS; - if (m_BindAddr.family() == AF_INET) - { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char*) &m_mcfg.iIpToS, &size); - } - else if (m_BindAddr.family() == AF_INET6) - { + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + socklen_t size = (socklen_t)sizeof m_mcfg.iIpToS; + if (m_BindAddr.family() == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char*)&m_mcfg.iIpToS, &size); + } + else if (m_BindAddr.family() == AF_INET6) + { #ifdef IPV6_TCLASS - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char*) &m_mcfg.iIpToS, &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char*)&m_mcfg.iIpToS, &size); #endif - } - else - { - // If family is unspecified, the socket probably doesn't exist. - LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - return m_mcfg.iIpToS; + } + else + { + // If family is unspecified, the socket probably doesn't exist. + LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + return m_mcfg.iIpToS; } #ifdef SRT_ENABLE_BINDTODEVICE @@ -478,7 +485,7 @@ bool srt::CChannel::getBind(char* dst, size_t len) // Try to obtain it directly from the function. If not possible, // then return from internal data. socklen_t length = len; - int res = ::getsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, dst, &length); + int res = ::getsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, dst, &length); if (res == -1) return false; // Happens on Linux v < 3.8 @@ -492,8 +499,8 @@ int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; - int res = ::ioctl(m_iSocket, type, &value); - if ( res != -1 ) + int res = ::ioctl(m_iSocket, type, &value); + if (res != -1) return value; #endif return -1; @@ -502,10 +509,10 @@ int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const int srt::CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) - int value = 0; - socklen_t len = sizeof (int); - int res = ::getsockopt(m_iSocket, level, option, &value, &len); - if ( res != -1 ) + int value = 0; + socklen_t len = sizeof(int); + int res = ::getsockopt(m_iSocket, level, option, &value, &len); + if (res != -1) return value; #endif return -1; @@ -517,26 +524,23 @@ void srt::CChannel::getSockAddr(sockaddr_any& w_addr) const // space to copy the socket name, it doesn't have to be correlated // with the address family. So the maximum space for any name, // regardless of the family, does the job. - socklen_t namelen = (socklen_t) w_addr.storage_size(); + socklen_t namelen = (socklen_t)w_addr.storage_size(); ::getsockname(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const { - socklen_t namelen = (socklen_t) w_addr.storage_size(); + socklen_t namelen = (socklen_t)w_addr.storage_size(); ::getpeername(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } - int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { - HLOGC(kslog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << addr.str() - << " target=@" << packet.m_iID - << " size=" << packet.getLength() - << " pkt.ts=" << packet.m_iTimeStamp - << " " << packet.Info()); + HLOGC(kslog.Debug, + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID + << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS @@ -546,18 +550,18 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const #undef FAKELOSS_STRING #undef FAKELOSS_WRAP - static int dcounter = 0; + static int dcounter = 0; static int flwcounter = 0; struct FakelossConfig { - pair config; + pair config; FakelossConfig(const char* f) { vector out; Split(f, '+', back_inserter(out)); - config.first = atoi(out[0].c_str()); + config.first = atoi(out[0].c_str()); config.second = out.size() > 1 ? atoi(out[1].c_str()) : 8; } }; @@ -571,7 +575,9 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { // This is a counter of how many packets in a row shall be lost --flwcounter; - HLOGC(kslog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter << " more to drop)"); + HLOGC(kslog.Debug, + log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter + << " more to drop)"); return packet.getLength(); // fake successful sendinf } @@ -583,7 +589,9 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > rnd) { dcounter = 1; - HLOGC(kslog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " << fakeloss.config.first << " more)"); + HLOGC(kslog.Debug, + log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " + << fakeloss.config.first << " more)"); flwcounter = fakeloss.config.first; return packet.getLength(); // fake successful sendinf } @@ -592,51 +600,51 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const #endif - // convert control information into network order - packet.toNL(); - - #ifndef _WIN32 - msghdr mh; - mh.msg_name = (sockaddr*)&addr; - mh.msg_namelen = addr.size(); - mh.msg_iov = (iovec*)packet.m_PacketVector; - mh.msg_iovlen = 2; - mh.msg_control = NULL; - mh.msg_controllen = 0; - mh.msg_flags = 0; - - const int res = ::sendmsg(m_iSocket, &mh, 0); - #else - DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength()); - int addrsize = addr.size(); - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL); - res = (0 == res) ? size : -1; - #endif - - packet.toHL(); - - return res; + // convert control information into network order + packet.toNL(); + +#ifndef _WIN32 + msghdr mh; + mh.msg_name = (sockaddr*)&addr; + mh.msg_namelen = addr.size(); + mh.msg_iov = (iovec*)packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + const int res = ::sendmsg(m_iSocket, &mh, 0); +#else + DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); + int addrsize = addr.size(); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL); + res = (0 == res) ? size : -1; +#endif + + packet.toHL(); + + return res; } EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const { - EReadStatus status = RST_OK; - int msg_flags = 0; - int recv_size = -1; + EReadStatus status = RST_OK; + int msg_flags = 0; + int recv_size = -1; #if defined(UNIX) || defined(_WIN32) - fd_set set; + fd_set set; timeval tv; FD_ZERO(&set); FD_SET(m_iSocket, &set); - tv.tv_sec = 0; - tv.tv_usec = 10000; - const int select_ret = ::select((int) m_iSocket + 1, &set, NULL, &set, &tv); + tv.tv_sec = 0; + tv.tv_usec = 10000; + const int select_ret = ::select((int)m_iSocket + 1, &set, NULL, &set, &tv); #else - const int select_ret = 1; // the socket is expected to be in the blocking mode itself + const int select_ret = 1; // the socket is expected to be in the blocking mode itself #endif - if (select_ret == 0) // timeout + if (select_ret == 0) // timeout { w_packet.setLength(-1); return RST_AGAIN; @@ -646,13 +654,13 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con if (select_ret > 0) { msghdr mh; - mh.msg_name = (w_addr.get()); - mh.msg_namelen = w_addr.size(); - mh.msg_iov = (w_packet.m_PacketVector); - mh.msg_iovlen = 2; - mh.msg_control = NULL; + mh.msg_name = (w_addr.get()); + mh.msg_namelen = w_addr.size(); + mh.msg_iov = (w_packet.m_PacketVector); + mh.msg_iovlen = 2; + mh.msg_control = NULL; mh.msg_controllen = 0; - mh.msg_flags = 0; + mh.msg_flags = 0; recv_size = ::recvmsg(m_iSocket, (&mh), 0); msg_flags = mh.msg_flags; @@ -683,7 +691,8 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con if (select_ret == -1 || recv_size == -1) { const int err = NET_ERROR; - if (err == EAGAIN || err == EINTR || err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call. + if (err == EAGAIN || err == EINTR || + err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call. { status = RST_AGAIN; } @@ -713,16 +722,23 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // value one Windows than 0, unless this procedure below is rewritten // to use WSARecvMsg(). - int recv_ret = SOCKET_ERROR; - DWORD flag = 0; + int recv_ret = SOCKET_ERROR; + DWORD flag = 0; - if (select_ret > 0) // the total number of socket handles that are ready + if (select_ret > 0) // the total number of socket handles that are ready { - DWORD size = (DWORD) (CPacket::HDR_SIZE + w_packet.getLength()); - int addrsize = w_addr.size(); - - recv_ret = ::WSARecvFrom(m_iSocket, ((LPWSABUF)w_packet.m_PacketVector), 2, - (&size), (&flag), (w_addr.get()), (&addrsize), NULL, NULL); + DWORD size = (DWORD)(CPacket::HDR_SIZE + w_packet.getLength()); + int addrsize = w_addr.size(); + + recv_ret = ::WSARecvFrom(m_iSocket, + ((LPWSABUF)w_packet.m_PacketVector), + 2, + (&size), + (&flag), + (w_addr.get()), + (&addrsize), + NULL, + NULL); if (recv_ret == 0) recv_size = size; } @@ -737,16 +753,9 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // WSAETIMEDOUT, which isn't mentioned in the documentation of WSARecvFrom at all. // // These below errors are treated as "fatal", all others are treated as "again". - static const int fatals [] = - { - WSAEFAULT, - WSAEINVAL, - WSAENETDOWN, - WSANOTINITIALISED, - WSA_OPERATION_ABORTED - }; + static const int fatals[] = {WSAEFAULT, WSAEINVAL, WSAENETDOWN, WSANOTINITIALISED, WSA_OPERATION_ABORTED}; static const int* fatals_end = fatals + Size(fatals); - const int err = NET_ERROR; + const int err = NET_ERROR; if (std::find(fatals, fatals_end, err) != fatals_end) { HLOGC(krlog.Debug, log << CONID() << "(sys)WSARecvFrom: " << SysStrError(err) << " [" << err << "]"); @@ -765,12 +774,12 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con msg_flags = 1; #endif - // Sanity check for a case when it didn't fill in even the header if (size_t(recv_size) < CPacket::HDR_SIZE) { status = RST_AGAIN; - HLOGC(krlog.Debug, log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes"); + HLOGC(krlog.Debug, + log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes"); goto Return_error; } @@ -791,10 +800,11 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // When this happens, then you have at best a fragment of the buffer and it's // useless anyway. This is solved by dropping the packet and fake that no // packet was received, so the packet will be then retransmitted. - if ( msg_flags != 0 ) + if (msg_flags != 0) { - HLOGC(krlog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size - << " msg_flags=0x" << hex << msg_flags << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")"); + HLOGC(krlog.Debug, + log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" << hex << msg_flags + << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")"); status = RST_AGAIN; goto Return_error; } @@ -803,21 +813,21 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // convert back into local host order // XXX use NtoHLA(). - //for (int i = 0; i < 4; ++ i) + // 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) + for (size_t i = 0; i < SRT_PH_E_SIZE; ++i) { *p = ntohl(*p); - ++ 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)); + 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)); } return RST_OK; diff --git a/srtcore/channel.h b/srtcore/channel.h index ad6b3f785..0255102fe 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -59,107 +59,107 @@ modified by #include "socketconfig.h" #include "netinet_any.h" -namespace srt { +namespace srt +{ class CChannel { - void createSocket(int family); -public: + void createSocket(int family); - // XXX There's currently no way to access the socket ID set for - // whatever the channel is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } +public: + // XXX There's currently no way to access the socket ID set for + // whatever the channel is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - CChannel(); - ~CChannel(); + CChannel(); + ~CChannel(); - /// Open a UDP channel. - /// @param [in] addr The local address that UDP will use. + /// Open a UDP channel. + /// @param [in] addr The local address that UDP will use. - void open(const sockaddr_any& addr); + void open(const sockaddr_any& addr); - void open(int family); + void open(int family); - /// Open a UDP channel based on an existing UDP socket. - /// @param [in] udpsock UDP socket descriptor. + /// Open a UDP channel based on an existing UDP socket. + /// @param [in] udpsock UDP socket descriptor. - void attach(UDPSOCKET udpsock, const sockaddr_any& adr); + void attach(UDPSOCKET udpsock, const sockaddr_any& adr); - /// Disconnect and close the UDP entity. + /// Disconnect and close the UDP entity. - void close() const; + void close() const; - /// Get the UDP sending buffer size. - /// @return Current UDP sending buffer size. + /// Get the UDP sending buffer size. + /// @return Current UDP sending buffer size. - int getSndBufSize(); + int getSndBufSize(); - /// Get the UDP receiving buffer size. - /// @return Current UDP receiving buffer size. + /// Get the UDP receiving buffer size. + /// @return Current UDP receiving buffer size. - int getRcvBufSize(); + int getRcvBufSize(); - /// Query the socket address that the channel is using. - /// @param [out] addr pointer to store the returned socket address. + /// Query the socket address that the channel is using. + /// @param [out] addr pointer to store the returned socket address. - void getSockAddr(sockaddr_any& addr) const; + void getSockAddr(sockaddr_any& addr) const; - /// Query the peer side socket address that the channel is connect to. - /// @param [out] addr pointer to store the returned socket address. + /// Query the peer side socket address that the channel is connect to. + /// @param [out] addr pointer to store the returned socket address. - void getPeerAddr(sockaddr_any& addr) const; + void getPeerAddr(sockaddr_any& addr) const; - /// Send a packet to the given address. - /// @param [in] addr pointer to the destination address. - /// @param [in] packet reference to a CPacket entity. - /// @return Actual size of data sent. + /// Send a packet to the given address. + /// @param [in] addr pointer to the destination address. + /// @param [in] packet reference to a CPacket entity. + /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; - /// Receive a packet from the channel and record the source address. - /// @param [in] addr pointer to the source address. - /// @param [in] packet reference to a CPacket entity. - /// @return Actual size of data received. + /// Receive a packet from the channel and record the source address. + /// @param [in] addr pointer to the source address. + /// @param [in] packet reference to a CPacket entity. + /// @return Actual size of data received. - EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; + EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; - void setConfig(const CSrtMuxerConfig& config); + void setConfig(const CSrtMuxerConfig& config); - /// Get the IP TTL. - /// @param [in] ttl IP Time To Live. - /// @return TTL. + /// Get the IP TTL. + /// @param [in] ttl IP Time To Live. + /// @return TTL. - int getIpTTL() const; + int getIpTTL() const; - /// Get the IP Type of Service. - /// @return ToS. + /// Get the IP Type of Service. + /// @return ToS. - int getIpToS() const; + int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE - bool getBind(char* dst, size_t len); + bool getBind(char* dst, size_t len); #endif - int ioctlQuery(int type) const; - int sockoptQuery(int level, int option) const; + int ioctlQuery(int type) const; + int sockoptQuery(int level, int option) const; - const sockaddr* bindAddress() { return m_BindAddr.get(); } - const sockaddr_any& bindAddressAny() { return m_BindAddr; } + const sockaddr* bindAddress() { return m_BindAddr.get(); } + const sockaddr_any& bindAddressAny() { return m_BindAddr; } private: - void setUDPSockOpt(); + void setUDPSockOpt(); private: + UDPSOCKET m_iSocket; // socket descriptor - UDPSOCKET m_iSocket; // socket descriptor - - // Mutable because when querying original settings - // this comprises the cache for extracted values, - // although the object itself isn't considered modified. - mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. - sockaddr_any m_BindAddr; + // Mutable because when querying original settings + // this comprises the cache for extracted values, + // although the object itself isn't considered modified. + mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. + sockaddr_any m_BindAddr; }; } // namespace srt From 44eb6cedebb14aeecb66066d3d590aeb127b2e6e Mon Sep 17 00:00:00 2001 From: LELEGARD Thierry Date: Thu, 27 May 2021 11:35:01 +0200 Subject: [PATCH 187/790] [build] Improved Windows installer - More comprehensive README. - Added a sample installation script for users to automate the download and installation of libsrt. Useful to automate user's CI/CD pipeline. - Renamed typo in script name ("intaller"). - Updated build script to also produce the .zip archive as published with version 1.4.3. But note that binaries don't compress well (only 2% space) and publishing a .zip instead of the .exe does not provide much improvement anyway. --- scripts/win-installer/README.md | 122 +++++++++++++--- ...n-intaller.ps1 => build-win-installer.ps1} | 15 ++ scripts/win-installer/install-libsrt.ps1 | 131 ++++++++++++++++++ 3 files changed, 252 insertions(+), 16 deletions(-) rename scripts/win-installer/{build-win-intaller.ps1 => build-win-installer.ps1} (93%) create mode 100644 scripts/win-installer/install-libsrt.ps1 diff --git a/scripts/win-installer/README.md b/scripts/win-installer/README.md index 205e03893..ff54df258 100644 --- a/scripts/win-installer/README.md +++ b/scripts/win-installer/README.md @@ -1,33 +1,123 @@ -## SRT Static Libraries Installer for Windows +# SRT Static Libraries Installer for Windows This directory contains scripts to build a binary installer for libsrt on Windows systems for Visual Studio applications using SRT. -### Building Windows applications with libsrt +## SRT developer: Building the libsrt installer + +### Prerequisites + +These first two steps need to be executed once only. + +- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. + This can be done automatically by running the PowerShell script `install-openssl.ps1`. + +- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. + This can be done automatically by running the PowerShell script `install-nsis.ps1`. + +### Building the libsrt installer + +To build the libsrt installer, simply run the PowerShell script `build-win-installer.ps1`. +Running it without parameters, for instance launching it from the Windows Explorer, is +sufficient to build the installer. + +Optional parameters: + +- `-Version name` : + Use the specified string as version number for libsrt. By default, if the + current commit has a tag, use that tag (initial "v" removed, for instance + `1.4.3`). Otherwise, the defaut version is a detailed version number (most + recent version, number of commits since then, short commit SHA, for instance + `1.4.3-32-g22cc924`). Use that option if necessary to specify some other + non-standard form of version string. + +- `-NoPause` : + Do not wait for the user to press `` at end of execution. By default, + execute a `pause` instruction at the end of execution, which is useful + when the script was launched from Windows Explorer. Use that option when the + script is invoked from another PowerShell script. + +The installer is then available in the directory `installers`. + +The name of the installer is `libsrt-VERS.exe` where `VERS` is the SRT version number +(see the `-Version` option). + +The installer shall then be published as a release asset in the `srt` repository +on GitHub, either as `libsrt-VERS.exe` or `libsrt-VERS-win-installer.zip`. +In the latter case, the archive shall contain `libsrt-VERS.exe`. + +## SRT user: Using the libsrt installer -After installing the libsrt binary, an environment variable named `LIBSRT` is -defined to the installation root (typically `C:\Program Files (x86)\libsrt`). +### Installing the SRT libraries -In this directory, there is a Visual Studio property file named `libsrt.props`. -Simply reference this property file in your Visual Studio project to use libsrt. +To install the SRT libraries, simply run the `libsrt-VERS.exe` installer which is +available in the [SRT release area](https://github.com/Haivision/srt/releases). + +After installing the libsrt binaries, an environment variable named `LIBSRT` is +defined with the installation root (typically `C:\Program Files (x86)\libsrt`). + +If there is a need for automation, in a CI/CD pipeline for instance, the download +of the latest `libsrt-VERS.exe` and its installation can be automated using the +sample PowerShell script `install-libsrt.ps1` which is available in this directory. +This script may be freely copied in the user's build environment. + +When run without parameters (for instance from the Windows explorer), this +script downloads and installs the latest version of libsrt. + +Optional parameters: + +- `-Destination path` : + Specify a local directory where the libsrt package will be downloaded. + By default, use the `tmp` subdirectory from this script's directory. + +- `-ForceDownload` : + Force a download even if the package is already downloaded in the + destination path. Note that the latest version is always checked. + If a older package is already present but a newer one is available + online, the newer one is always downloaded, even without this option. + +- `-GitHubActions` : + When used in a GitHub Actions workflow, make sure that the `LIBSRT` + environment variable is propagated to subsequent jobs. In your GitHub + workflow, in the initial setup phase, use + `script-dir\install-libsrt.ps1 -GitHubActions -NoPause`. + +- `-NoInstall` : + Do not install the package, only download it. By default, libsrt is installed. + +- `-NoPause` : + Do not wait for the user to press `` at end of execution. By default, + execute a `pause` instruction at the end of execution, which is useful + when the script was launched from Windows Explorer. Use that option when the + script is invoked from another PowerShell script. + +### Building Windows applications with libsrt + +In the SRT installation root directory (specified in environment variable `LIBSRT`), +there is a Visual Studio property file named `libsrt.props`. Simply reference this +property file in your Visual Studio project to use libsrt. You can also do that manually by editing the application project file (the XML file named with a `.vcxproj` extension). Add the following line just before the end of the file: ~~~ - + ~~~ -### Building the installer +With this setup, just compile your application normally, either using the +Visual Studio IDE or the MSBuild command line tool. -The first two steps need to be executed once only. Only the last step needs -to be repeated each time a new version of libsrt is available. +## Files reference -- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. - This can be done automatically by running the PowerShell script `install-openssl.ps1`. -- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. - This can be done automatically by running the PowerShell script `install-nsis.ps1`. -- Build the libsrt installer by running the PowerShell script `build-win-installer.ps1`. +This directory contains the following files: -The installer is then available in the directory `installers`. +| File name | Usage +| ----------------------- | ----- +| build-win-installer.ps1 | PowerShell script to build the libsrt installer. +| install-libsrt.ps1 | Sample PowerShell script to automatically install libsrt (for user's projects). +| install-openssl.ps1 | PowerShell script to install OpenSSL (prerequisite to build the installer). +| install-nsis.ps1 | PowerShell script to install NSIS (prerequisite to build the installer). +| libsrt.nsi | NSIS installation script (used to build the installer). +| libsrt.props | Visual Studio property files to use libsrt (embedded in the installer). +| README.md | This text file. diff --git a/scripts/win-installer/build-win-intaller.ps1 b/scripts/win-installer/build-win-installer.ps1 similarity index 93% rename from scripts/win-installer/build-win-intaller.ps1 rename to scripts/win-installer/build-win-installer.ps1 index 261c457fa..4265edf8e 100644 --- a/scripts/win-installer/build-win-intaller.ps1 +++ b/scripts/win-installer/build-win-installer.ps1 @@ -200,6 +200,9 @@ if ($Missing -gt 0) { # Build the binary installer. #----------------------------------------------------------------------------- +$InstallExe = "$OutDir\libsrt-$Version.exe" +$InstallZip = "$OutDir\libsrt-$Version-win-installer.zip" + Write-Output "Building installer ..." & $NSIS /V2 ` /DVersion="$Version" ` @@ -209,4 +212,16 @@ Write-Output "Building installer ..." /DRepoDir="$RepoDir" ` "$ScriptDir\libsrt.nsi" +if (-not (Test-Path $InstallExe)) { + Exit-Script "**** Missing $InstallExe" +} + +Write-Output "Building installer archive ..." +Remove-Item -Force -ErrorAction SilentlyContinue $InstallZip +Compress-Archive -Path $InstallExe -DestinationPath $InstallZip -CompressionLevel Optimal + +if (-not (Test-Path $InstallZip)) { + Exit-Script "**** Missing $InstallZip" +} + Exit-Script diff --git a/scripts/win-installer/install-libsrt.ps1 b/scripts/win-installer/install-libsrt.ps1 new file mode 100644 index 000000000..ae1b8133e --- /dev/null +++ b/scripts/win-installer/install-libsrt.ps1 @@ -0,0 +1,131 @@ +ļ»æ# SRT library download and install for Windows. +# Copyright (c) 2021, Thierry Lelegard +# All rights reserved. + +<# + .SYNOPSIS + + Download and install the libsrt library for Windows. This script is + provided to automate the build of Windows applications using libsrt. + + .PARAMETER Destination + + Specify a local directory where the libsrt package will be downloaded. + By default, use "tmp" subdirectory from this script. + + .PARAMETER ForceDownload + + Force a download even if the package is already downloaded. + + .PARAMETER GitHubActions + + When used in a GitHub Actions workflow, make sure that the LIBSRT + environment variable is propagated to subsequent jobs. + + .PARAMETER NoInstall + + Do not install the package. By default, libsrt is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [string]$Destination = "", + [switch]$ForceDownload = $false, + [switch]$GitHubActions = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "libsrt download and installation procedure" + +# Default directory for downloaded products. +if (-not $Destination) { + $Destination = "$PSScriptRoot\tmp" +} + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the URL of the latest libsrt installer. +$URL = (Invoke-RestMethod "https://api.github.com/repos/Haivision/srt/releases?per_page=20" | + ForEach-Object { $_.assets } | + ForEach-Object { $_.browser_download_url } | + Select-String @("/libsrt-.*\.exe$", "/libsrt-.*-win-installer\.zip$") | + Select-Object -First 1) + +if (-not $URL) { + Exit-Script "Could not find a libsrt installer on GitHub" +} +if (-not ($URL -match "\.zip$") -and -not ($URL -match "\.exe$")) { + Exit-Script "Unexpected URL, not .exe, not .zip: $URL" +} + +# Installer name and path. +$InstName = (Split-Path -Leaf $URL) +$InstPath = "$Destination\$InstName" + +# Create the directory for downloaded products when necessary. +[void](New-Item -Path $Destination -ItemType Directory -Force) + +# Download installer +if (-not $ForceDownload -and (Test-Path $InstPath)) { + Write-Output "$InstName already downloaded, use -ForceDownload to download again" +} +else { + Write-Output "Downloading $URL ..." + Invoke-WebRequest $URL.ToString() -UseBasicParsing -UserAgent Download -OutFile $InstPath + if (-not (Test-Path $InstPath)) { + Exit-Script "$URL download failed" + } +} + +# If installer is an archive, expect an exe with same name inside. +if ($InstName -match "\.zip$") { + + # Expected installer name in archive. + $ZipName = $InstName + $ZipPath = $InstPath + $InstName = $ZipName -replace '-win-installer.zip','.exe' + $InstPath = "$Destination\$InstName" + + # Extract the installer. + Remove-Item -Force $InstPath -ErrorAction SilentlyContinue + Write-Output "Expanding $ZipName ..." + Expand-Archive $ZipPath -DestinationPath $Destination + if (-not (Test-Path $InstPath)) { + Exit-Script "$InstName not found in $ZipName" + } +} + +# Install libsrt +if (-not $NoInstall) { + Write-Output "Installing $InstName" + Start-Process -FilePath $InstPath -ArgumentList @("/S") -Wait +} + +# Propagate LIBSRT in next jobs for GitHub Actions. +if ($GitHubActions -and (-not -not $env:GITHUB_ENV) -and (Test-Path $env:GITHUB_ENV)) { + $libsrt = [System.Environment]::GetEnvironmentVariable("LIBSRT","Machine") + Write-Output "LIBSRT=$libsrt" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append +} + +Exit-Script From e2a00aa03a36e153ab8cfe2c0790f7bf43747d8d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 28 May 2021 11:04:53 +0200 Subject: [PATCH 188/790] [core] Detect reusable bindings and binding conflicts correctly (#1588). Introduced new SRT socket error SRT_EBINDCONFLICT. --- .travis.yml | 5 +- docs/API/API-functions.md | 86 ++++-- scripts/generate-error-types.tcl | 18 +- srtcore/api.cpp | 189 +++++++++---- srtcore/api.h | 5 + srtcore/srt.h | 2 + srtcore/strerror_defs.cpp | 12 +- test/filelist.maf | 1 + test/test_reuseaddr.cpp | 440 +++++++++++++++++++++++++++++++ test/test_sync.cpp | 4 +- 10 files changed, 672 insertions(+), 90 deletions(-) create mode 100644 test/test_reuseaddr.cpp diff --git a/.travis.yml b/.travis.yml index 4ffe49add..51631c086 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ matrix: - make - cd .. env: BUILD_TYPE=Release - + # Power jobs - os: linux arch: ppc64le @@ -69,6 +69,7 @@ matrix: - BUILD_TYPE=Release - BUILD_OPTS='-DENABLE_MONOTONIC_CLOCK=ON' script: + - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*:ReuseAddr.ProtocolVersion" ; # Tests to skip due to lack of IPv6 support - if [ "$TRAVIS_COMPILER" == "x86_64-w64-mingw32-g++" ]; then export CC="x86_64-w64-mingw32-gcc"; export CXX="x86_64-w64-mingw32-g++"; @@ -91,7 +92,7 @@ script: make -j$(nproc); fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then - ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; + ./test-srt --gtest_filter="-$TESTS_IPv6"; fi - if (( "$RUN_CODECOV" )); then source ./scripts/collect-gcov.sh; diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 44f8ac19a..5a1fffd70 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -384,13 +384,14 @@ connecting, use [`srt_connect_bind`](#srt_connect_bind) for that purpose. | `SRT_ERROR` | (-1) on error, otherwise 0 | | | | -| Errors | | -|:----------------------------------- |:-------------------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | -| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | -| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | -| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | -| | | +| Errors | | +|:---------------------------------------- |:-------------------------------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | +| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | +| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | +| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | +| [`SRT_EBINDCONFLICT`](#srt_ebindconflict)| Binding specification conflicts with existing one | +| | | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -841,18 +842,19 @@ first on the automatically created socket for the connection. | `SRT_ERROR` | (-1) in case of error | | 0 | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | -| | | +| | | -| Errors | | -|:------------------------------------- |:-------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | -| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | -| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | -| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | -| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Internal error ([`srt_connect`](#srt_connect) should not report it after [`srt_bind`](#srt_bind) was called) | -| [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | -| [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | -| | | +| Errors | | +|:---------------------------------------- |:-------------------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | +| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | +| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | +| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | +| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Internal error ([`srt_connect`](#srt_connect) should not report it after [`srt_bind`](#srt_bind) was called) | +| [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | +| [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | +| [`SRT_EBINDCONFLICT`](#srt_ebindconflict)| Binding specification conflicts with existing one | +| | | **IMPORTANT**: It's not allowed to bind and connect the same socket to two @@ -3225,6 +3227,54 @@ by setting the `SRT_EPOLL_ENABLE_EMPTY` flag, which may be useful when you use multiple threads and start waiting without subscribed sockets, so that you can subscribe them later from another thread. + +#### `SRT_EBINDCONFLICT` + +The binding you are attempting to set up a socket with cannot be completed because +it conflicts with another existing binding. This is because an intersecting binding +was found that cannot be reused according to the specification in `srt_bind` call. + +A binding is considered intersecting if the existing binding has the same port +and covers at least partially the range as that of the attempted binding. These +ranges can be split in three groups: + +1. An explicitly specified IP address (both IPv4 and IPv6) covers this address only. +2. An IPv4 wildcard 0.0.0.0 covers all IPv4 addresses (but not IPv6). +3. An IPv6 wildcard :: covers: + * if `SRTO_IPV6ONLY` is true - all IPv6 addresses (but not IPv4) + * if `SRTO_IPV6ONLY` is false - all IP addresses. + +Example 1: + +* Socket 1: bind to IPv4 0.0.0.0 +* Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = true +* Result: NOT intersecting + +Example 2: + +* Socket 1: bind to IPv4 1.2.3.4 +* Socket 2: bind to IPv4 0.0.0.0 +* Result: intersecting (and conflicting) + +Example 3: + +* Socket 1: bind to IPv4 1.2.3.4 +* Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = false +* Result: intersecting (and conflicting) + +If any common range coverage is found between the attempted binding specification +(in `srt_bind` call) and the found existing binding with the same port number, +then all of the following conditions must be satisfied between them: + +1. The `SRTO_REUSEADDR` must be true (default) in both. + +2. The IP address specification (in case of IPv6, also including the value of +`SRTO_IPV6ONLY` flag) must be exactly identical. + +3. The UDP-specific settings must be identical. + +If any of these conditions isn't satisfied, the `srt_bind` function results +in conflict and report this error. #### SRT_EASYNCFAIL diff --git a/scripts/generate-error-types.tcl b/scripts/generate-error-types.tcl index 15c8c0fa2..b51d60eb1 100755 --- a/scripts/generate-error-types.tcl +++ b/scripts/generate-error-types.tcl @@ -61,6 +61,7 @@ set code_minor { XSIZE 12 EIDINVAL 13 EEMPTY 14 + BUSYPORT 15 WRAVAIL 1 RDAVAIL 2 @@ -121,7 +122,7 @@ set errortypes { XSIZE "Message is too large to send (it must be less than the SRT send buffer size)" EIDINVAL "Invalid epoll ID" EEMPTY "All sockets removed from epoll, waiting would deadlock" - + BUSYPORT "Another socket is bound to that port and is not reusable for requested settings" } AGAIN "Non-blocking call failure" { @@ -142,11 +143,10 @@ set main_array_item { const char** strerror_array_major [] = { $minor_array_list }; - } set major_size_item { -size_t strerror_array_sizes [] = { +const size_t strerror_array_sizes [] = { $minor_array_sizes }; } @@ -155,11 +155,9 @@ set minor_array_item { const char* strerror_msgs_$majorlc [] = { $minor_message_items }; - } set strerror_function { - const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; @@ -228,16 +226,17 @@ proc Generate:imp {} { puts [subst -nobackslashes -nocommands $::minor_array_item] append minor_array_list " strerror_msgs_$majorlc, // MJ_$mt = $majitem\n" - append minor_array_sizes " [expr {$minitem}],\n" + #append minor_array_sizes " [expr {$minitem}],\n" + append minor_array_sizes " SRT_ARRAY_SIZE(strerror_msgs_$majorlc) - 1,\n" incr majitem } - append minor_array_list " NULL\n" - append minor_array_sizes " 0\n" + append minor_array_list " NULL" + append minor_array_sizes " 0" puts [subst -nobackslashes -nocommands $::main_array_item] + puts {#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0])} puts [subst -nobackslashes -nocommands $::major_size_item] - puts "" puts $::strerror_function puts "\} // namespace srt" @@ -250,4 +249,3 @@ if {[lindex $argv 0] != ""} { } Generate:$defmode - diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 44a068764..ad243d50e 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2829,51 +2829,150 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) } } -void srt::CUDTUnited::updateMux( - CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) +void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { - ScopedLock cg(m_GlobControlLock); + w_m.m_mcfg = s->m_pUDT->m_config; + w_m.m_iIPversion = af; + w_m.m_iRefCount = 1; + w_m.m_iID = s->m_SocketID; +} - // Don't try to reuse given address, if udpsock was given. - // In such a case rely exclusively on that very socket and - // use it the way as it is configured, of course, create also - // always a new multiplexer for that very socket. - if (!udpsock && s->m_pUDT->m_config.bReuseAddr) - { - const int port = addr.hport(); +uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) +{ + w_s->m_pUDT->m_pSndQueue = fw_sm.m_pSndQueue; + w_s->m_pUDT->m_pRcvQueue = fw_sm.m_pRcvQueue; + w_s->m_iMuxID = fw_sm.m_iID; + sockaddr_any sa; + fw_sm.m_pChannel->getSockAddr((sa)); + w_s->m_SelfAddr = sa; // Will be also completed later, but here it's needed for later checks + return sa.hport(); +} - // find a reusable address - for (map::iterator i = m_mMultiplexer.begin(); - i != m_mMultiplexer.end(); ++ i) - { - // Use the "family" value blindly from the address; we - // need to find an existing multiplexer that binds to the - // given port in the same family as requested address. - if ((i->second.m_iIPversion == addr.family()) - && i->second.m_mcfg == s->m_pUDT->m_config) - { - if (i->second.m_iPort == port) +bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) +{ + return m.m_mcfg.bReuseAddr && m.m_mcfg == s->m_pUDT->m_config; +} + +void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) +{ + ScopedLock cg(m_GlobControlLock); + + // If udpsock is provided, then this socket will be simply + // taken for binding as a good deal. It would be nice to make + // a sanity check to see if this UDP socket isn't already installed + // in some multiplexer, but we state this UDP socket isn't accessible + // anyway so this wouldn't be possible. + if (!udpsock) + { + // If not, we need to see if there exist already a multiplexer bound + // to the same endpoint. + const int port = addr.hport(); + + bool reuse_attempt = false; + for (map::iterator i = m_mMultiplexer.begin(); + i != m_mMultiplexer.end(); ++ i) + { + CMultiplexer& m = i->second; + + // First, we need to find a multiplexer with the same port. + if (m.m_iPort != port) { - // HLOGF(smlog.Debug, "reusing multiplexer for port - // %hd\n", port); - // reuse the existing multiplexer - ++ i->second.m_iRefCount; - s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; - s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; - s->m_iMuxID = i->second.m_iID; - s->m_SelfAddr.family(addr.family()); - return; + HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID << " found, but for port " + << m.m_iPort << " (requested port: " << port << ")"); + continue; } - } - } - } + + // If this is bound to the wildcard address, it can be reused if: + // - addr is also a wildcard + // - channel settings match + // Otherwise it's a conflict. + sockaddr_any sa; + m.m_pChannel->getSockAddr((sa)); + + HLOGC(smlog.Debug, log << "bind: Found existing muxer @" << m.m_iID << " : " << sa.str() + << " - check against " << addr.str()); + + if (sa.isany()) + { + if (!addr.isany()) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with existing wildcard binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + + // Still, for ANY you need either the same family, or open + // for families. + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->m_pUDT->m_config.iIpV6Only) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with existing IPv6 wildcard binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + + if ((m.m_mcfg.iIpV6Only == 0 || s->m_pUDT->m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with IPv6 wildcard binding: " << sa.str() + << " : family " << (m.m_iIPversion == AF_INET ? "IPv4" : "IPv6") + << " vs. " << (addr.family() == AF_INET ? "IPv4" : "IPv6")); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + reuse_attempt = true; + HLOGC(smlog.Debug, log << "bind: wildcard address - multiplexer reusable"); + } + else if (addr.isany() && addr.family() == sa.family()) + { + LOGC(smlog.Error, log << "bind: Wildcard address: " << addr.str() + << " conflicts with existting IP binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + // If this is bound to a certain address, AND: + else if (sa.equal_address(addr)) + { + // - the address is the same as addr + reuse_attempt = true; + HLOGC(smlog.Debug, log << "bind: same IP address - multiplexer reusable"); + } + else + { + HLOGC(smlog.Debug, log << "bind: IP addresses differ - ALLOWED to create a new multiplexer"); + } + // Otherwise: + // - the address is different than addr + // - the address can't be reused, but this can go on with new one. + + // If this is a reusage attempt: + if (reuse_attempt) + { + // - if the channel settings match, it can be reused + if (channelSettingsMatch(m, s)) + { + HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port); + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + installMuxer((s), (i->second)); + return; + } + else + { + // - if not, it's a conflict + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with binding: " << sa.str() << " due to channel settings"); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } + // If not, proceed to the next one, and when there are no reusage + // candidates, proceed with creating a new multiplexer. + + // Note that a binding to a different IP address is not treated + // as a candidate for either reuseage or conflict. + } + } // a new multiplexer is needed CMultiplexer m; - m.m_mcfg = s->m_pUDT->m_config; - m.m_iIPversion = addr.family(); - m.m_iRefCount = 1; - m.m_iID = s->m_SocketID; + configureMuxer((m), s, addr.family()); try { @@ -2903,13 +3002,7 @@ void srt::CUDTUnited::updateMux( m.m_pChannel->open(addr); } - sockaddr_any sa; - m.m_pChannel->getSockAddr((sa)); - m.m_iPort = sa.hport(); - s->m_SelfAddr = sa; // Will be also completed later, but here it's needed for later checks - m.m_pTimer = new CTimer; - m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; @@ -2917,11 +3010,10 @@ void srt::CUDTUnited::updateMux( 32, s->m_pUDT->maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + // Rewrite the port here, as it might be only known upon return + // from CChannel::open. + m.m_iPort = installMuxer((s), m); m_mMultiplexer[m.m_iID] = m; - - s->m_pUDT->m_pSndQueue = m.m_pSndQueue; - s->m_pUDT->m_pRcvQueue = m.m_pRcvQueue; - s->m_iMuxID = m.m_iID; } catch (const CUDTException&) { @@ -2934,8 +3026,7 @@ void srt::CUDTUnited::updateMux( throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); } - HLOGF(smlog.Debug, - "creating new multiplexer for port %i\n", m.m_iPort); + HLOGC(smlog.Debug, log << "bind: creating new multiplexer for port " << m.m_iPort); } // This function is going to find a multiplexer for the port contained diff --git a/srtcore/api.h b/srtcore/api.h index 3ef75c6e7..4228d8b46 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -408,6 +408,11 @@ friend class CRendezvousQueue; void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); + // Utility functions for updateMux + void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); + uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm); + bool channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s); + private: std::map m_mMultiplexer; // UDP multiplexer sync::Mutex m_MultiplexerLock; diff --git a/srtcore/srt.h b/srtcore/srt.h index 21e94879e..5d3349a48 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -473,6 +473,7 @@ enum CodeMinor MN_XSIZE = 12, MN_EIDINVAL = 13, MN_EEMPTY = 14, + MN_BUSYPORT = 15, // MJ_AGAIN MN_WRAVAIL = 1, MN_RDAVAIL = 2, @@ -528,6 +529,7 @@ typedef enum SRT_ERRNO SRT_ELARGEMSG = MN(NOTSUP, XSIZE), SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL), SRT_EPOLLEMPTY = MN(NOTSUP, EEMPTY), + SRT_EBINDCONFLICT = MN(NOTSUP, BUSYPORT), SRT_EASYNCFAIL = MJ(AGAIN), SRT_EASYNCSND = MN(AGAIN, WRAVAIL), diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index c37ee276d..7393c1279 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -19,7 +19,6 @@ const char* strerror_msgs_success [] = { "" }; - // MJ_SETUP 'Connection setup failure' const char* strerror_msgs_setup [] = { @@ -32,7 +31,6 @@ const char* strerror_msgs_setup [] = { "" }; - // MJ_CONNECTION '' const char* strerror_msgs_connection [] = { @@ -42,7 +40,6 @@ const char* strerror_msgs_connection [] = { "" }; - // MJ_SYSTEMRES 'System resource failure' const char* strerror_msgs_systemres [] = { @@ -53,7 +50,6 @@ const char* strerror_msgs_systemres [] = { "" }; - // MJ_FILESYSTEM 'File system failure' const char* strerror_msgs_filesystem [] = { @@ -65,7 +61,6 @@ const char* strerror_msgs_filesystem [] = { "" }; - // MJ_NOTSUP 'Operation not supported' const char* strerror_msgs_notsup [] = { @@ -84,10 +79,10 @@ const char* strerror_msgs_notsup [] = { "Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12 "Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13 "Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14 + "Operation not supported: Another socket is bound to that port and is not reusable for requested settings", // MN_BUSYPORT = 15 "" }; - // MJ_AGAIN 'Non-blocking call failure' const char* strerror_msgs_again [] = { @@ -99,7 +94,6 @@ const char* strerror_msgs_again [] = { "" }; - // MJ_PEERERROR 'The peer side has signaled an error' const char* strerror_msgs_peererror [] = { @@ -108,7 +102,6 @@ const char* strerror_msgs_peererror [] = { }; - const char** strerror_array_major [] = { strerror_msgs_success, // MJ_SUCCESS = 0 strerror_msgs_setup, // MJ_SETUP = 1 @@ -123,7 +116,7 @@ const char** strerror_array_major [] = { #define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0]) -const size_t strerror_array_sizes[] = { +const size_t strerror_array_sizes [] = { SRT_ARRAY_SIZE(strerror_msgs_success) - 1, SRT_ARRAY_SIZE(strerror_msgs_setup) - 1, SRT_ARRAY_SIZE(strerror_msgs_connection) - 1, @@ -135,6 +128,7 @@ const size_t strerror_array_sizes[] = { 0 }; + const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; diff --git a/test/filelist.maf b/test/filelist.maf index 77f0d4b1f..d92cabd9d 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -20,6 +20,7 @@ test_sync.cpp test_timer.cpp test_unitqueue.cpp test_utilities.cpp +test_reuseaddr.cpp # Tests for bonding only - put here! diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp new file mode 100644 index 000000000..18ab5fdfa --- /dev/null +++ b/test/test_reuseaddr.cpp @@ -0,0 +1,440 @@ +#include "gtest/gtest.h" +#include +#ifndef _WIN32 +#include +#endif + +#include "common.h" +#include "srt.h" +#include "udt.h" + +// Copied from ../apps/apputil.cpp, can't really link this file here. +sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_family = AF_INET) +{ + using namespace std; + + // Handle empty name. + // If family is specified, empty string resolves to ANY of that family. + // If not, it resolves to IPv4 ANY (to specify IPv6 any, use [::]). + if (name == "") + { + sockaddr_any result(pref_family == AF_INET6 ? pref_family : AF_INET); + result.hport(port); + return result; + } + + bool first6 = pref_family != AF_INET; + int families[2] = {AF_INET6, AF_INET}; + if (!first6) + { + families[0] = AF_INET; + families[1] = AF_INET6; + } + + for (int i = 0; i < 2; ++i) + { + int family = families[i]; + sockaddr_any result (family); + + // Try to resolve the name by pton first + if (inet_pton(family, name.c_str(), result.get_addr()) == 1) + { + result.hport(port); // same addr location in ipv4 and ipv6 + return result; + } + } + + // If not, try to resolve by getaddrinfo + // This time, use the exact value of pref_family + + sockaddr_any result; + addrinfo fo = { + 0, + pref_family, + 0, 0, + 0, 0, + NULL, NULL + }; + + addrinfo* val = nullptr; + int erc = getaddrinfo(name.c_str(), nullptr, &fo, &val); + if (erc == 0) + { + result.set(val->ai_addr); + result.len = result.size(); + result.hport(port); // same addr location in ipv4 and ipv6 + } + freeaddrinfo(val); + + return result; +} + +#ifdef _WIN32 + +// On Windows there's a function for it, but it requires an extra +// iphlp library to be attached to the executable, which is kinda +// problematic. Temporarily block tests using this function on Windows. + +std::string GetLocalIP() +{ + std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n"; + return ""; +} +#else +struct IfAddr +{ + ifaddrs* handle; + IfAddr() + { + getifaddrs(&handle); + } + + ~IfAddr() + { + freeifaddrs(handle); + } +}; + +std::string GetLocalIP() +{ + struct ifaddrs * ifa=NULL; + void * tmpAddrPtr=NULL; + + IfAddr ifAddr; + + for (ifa = ifAddr.handle; ifa != NULL; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) + { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) + { + // is a valid IP4 Address + sockaddr_in* psin = (struct sockaddr_in *)ifa->ifa_addr; + tmpAddrPtr=&psin->sin_addr; + + if (ntohl(psin->sin_addr.s_addr) == 0x7f000001) // Skip 127.0.0.1 + continue; + + char addressBuffer[INET_ADDRSTRLEN] = ""; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + return addressBuffer; + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 + // is a valid IP6 Address + tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN] = ""; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + return addressBuffer; + } + } + + return ""; +} +#endif + +int client_pollid = SRT_ERROR; +SRTSOCKET m_client_sock = SRT_ERROR; + +void clientSocket(std::string ip, int port, bool expect_success) +{ + int yes = 1; + int no = 0; + + int family = AF_INET; + if (ip.substr(0, 2) == "6.") + { + family = AF_INET6; + ip = ip.substr(2); + } + + m_client_sock = srt_create_socket(); + ASSERT_NE(m_client_sock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + + int epoll_out = SRT_EPOLL_OUT; + srt_epoll_add_usock(client_pollid, m_client_sock, &epoll_out); + + sockaddr_any sa = CreateAddr(ip, port, family); + + std::cout << "Connecting to: " << sa.str() << std::endl; + + int connect_res = srt_connect(m_client_sock, sa.get(), sa.size()); + + if (connect_res == -1) + { + std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl; + } + + if (expect_success) + { + EXPECT_NE(connect_res, -1); + // Socket readiness for connection is checked by polling on WRITE allowed sockets. + + { + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + 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); + + + 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], m_client_sock); // for our client socket + } + + char buffer[1316] = {1, 2, 3, 4}; + EXPECT_NE(srt_sendmsg(m_client_sock, buffer, sizeof buffer, + -1, // infinit ttl + true // in order must be set to true + ), + SRT_ERROR); + } + else + { + EXPECT_EQ(connect_res, -1); + } + + std::cout << "Client exit\n"; +} + +int server_pollid = SRT_ERROR; + +void serverSocket(std::string ip, int port, bool expect_success) +{ + int yes = 1; + int no = 0; + + SRTSOCKET m_bindsock = srt_create_socket(); + ASSERT_NE(m_bindsock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + + int epoll_in = SRT_EPOLL_IN; + srt_epoll_add_usock(server_pollid, m_bindsock, &epoll_in); + + sockaddr_any sa = CreateAddr(ip, port); + + std::cout << "Bind to: " << sa.str() << std::endl; + + int bind_res = srt_bind(m_bindsock, sa.get(), sa.size()); + if (!expect_success) + { + std::cout << "Binding should fail: " << srt_getlasterror_str() << std::endl; + ASSERT_EQ(bind_res, SRT_ERROR); + return; + } + + ASSERT_NE(bind_res, SRT_ERROR); + + ASSERT_NE(srt_listen(m_bindsock, SOMAXCONN), SRT_ERROR); + + if (ip == "0.0.0.0") + ip = "127.0.0.1"; // override wildcard + else if ( ip == "::") + ip = "::1"; + + std::thread client(clientSocket, ip, port, expect_success); + + { // wait for connection from client + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + ASSERT_NE(srt_epoll_wait(server_pollid, + read, &rlen, + write, &wlen, + 3000, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR ); + + + 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], m_bindsock); // read event is for bind socket + } + + sockaddr_any scl; + + SRTSOCKET m_sock = srt_accept(m_bindsock, scl.get(), &scl.len); + if (m_sock == -1) + { + std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; + } + ASSERT_NE(m_sock, SRT_INVALID_SOCK); + + sockaddr_any showacp = (sockaddr*)&scl; + std::cout << "Accepted from: " << showacp.str() << std::endl; + + srt_epoll_add_usock(server_pollid, m_sock, &epoll_in); // wait for input + + char buffer[1316]; + { // wait for 1316 packet from client + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + 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 ); + + + 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], m_sock); // read event is for bind socket + } + + char pattern[4] = {1, 2, 3, 4}; + + ASSERT_EQ(srt_recvmsg(m_sock, buffer, sizeof buffer), + 1316); + + EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); + + client.join(); + srt_close(m_sock); + srt_close(m_bindsock); + srt_close(m_client_sock); // cannot close m_client_sock after srt_sendmsg because of issue in api.c:2346 + + std::cout << "Server exit\n"; +} + +TEST(ReuseAddr, SameAddr1) +{ + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + std::thread server_1(serverSocket, "127.0.0.1", 5000, true); + server_1.join(); + + std::thread server_2(serverSocket, "127.0.0.1", 5000, true); + server_2.join(); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, SameAddr2) +{ + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + std::thread server_1(serverSocket, localip, 5000, true); + server_1.join(); + + std::thread server_2(serverSocket, localip, 5000, true); + server_2.join(); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, DiffAddr) +{ + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("127.0.0.1", 5000, true); + serverSocket(localip, 5000, true); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, Wildcard) +{ +#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 + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("0.0.0.0", 5000, true); + serverSocket(localip, 5000, false); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + + +TEST(ReuseAddr, ProtocolVersion) +{ +#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 + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("::", 5000, true); + serverSocket("0.0.0.0", 5000, true); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 950af154a..4967fbf86 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -449,8 +449,8 @@ TEST(SyncEvent, WaitForTwoNotifyOne) using wait_t = decltype(future_t().wait_for(chrono::microseconds(0))); wait_t wait_state[2] = { - move(future_result[0].wait_for(chrono::microseconds(100))), - move(future_result[1].wait_for(chrono::microseconds(100))) + move(future_result[0].wait_for(chrono::microseconds(500))), + move(future_result[1].wait_for(chrono::microseconds(500))) }; cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size() From 17fee159f9089e84cb2115747ba89c8d9ea7386a Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Mon, 31 May 2021 19:45:33 +0800 Subject: [PATCH 189/790] [core] make sure TTL will not drop packets over last block (#2005) --- srtcore/buffer.cpp | 11 +++++++++-- srtcore/buffer.h | 2 +- srtcore/core.cpp | 7 ------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 5a756792e..db3102e7a 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -522,8 +522,15 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: // XXX Suboptimal procedure to keep the blocks identifiable // by sequence number. Consider using some circular buffer. - for (int i = 0; i < offset; ++i) + for (int i = 0; i < offset && p != m_pLastBlock; ++i) + { p = p->m_pNext; + } + if (p == m_pLastBlock) + { + LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); + return 0; + } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; @@ -550,7 +557,7 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: w_msglen = 1; p = p->m_pNext; bool move = false; - while (msgno == p->getMsgSeq()) + while (p != m_pLastBlock && msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9a92e25b0..f69bef1d9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -153,7 +153,7 @@ class CSndBuffer /// @param [out] msgno message number of the packet. /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message - /// @return Actual length of data read. + /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// Get the time of the last retransmission (if any) of the DATA packet. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1e9d2f052..826e9c18d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8882,7 +8882,6 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi int msglen; const int payload = m_pSndBuffer->readData(offset, (w_packet), (w_origintime), (msglen)); - SRT_ASSERT(payload != 0); if (payload == -1) { int32_t seqpair[2]; @@ -8903,12 +8902,6 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi continue; } - // NOTE: This is just a sanity check. Returning 0 is impossible to happen - // in case of retransmission. If the offset was a positive value, then the - // block must exist in the old blocks because it wasn't yet cut off by ACK - // and has been already recorded as sent (otherwise the peer wouldn't send - // back the loss report). May something happen here in case when the send - // loss record has been updated by the FASTREXMIT. else if (payload == 0) continue; From 1751babcbe7758ec515d2ec6f2d1bac0f5e94991 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 31 May 2021 16:04:02 +0200 Subject: [PATCH 190/790] [core] FIX: Added check for NULL unit when passing to updateConnStatus (#2028) --- srtcore/core.cpp | 48 +++++++++++++++++++++++++++++------------------ srtcore/core.h | 6 +++--- srtcore/queue.cpp | 20 ++++++++++++-------- srtcore/queue.h | 2 +- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 826e9c18d..90162fa89 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3600,7 +3600,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // it means that it has done all that was required, however none of the below // things has to be done (this function will do it by itself if needed). // Otherwise the handshake rolling can be interrupted and considered complete. - cst = processRendezvous(response, serv_addr, RST_OK, (reqpkt)); + cst = processRendezvous(&response, serv_addr, RST_OK, (reqpkt)); if (cst == CONN_CONTINUE) continue; break; @@ -3744,7 +3744,7 @@ EConnectStatus srt::CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NO bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, - const CPacket& response, + const CPacket* pResponse /*[[nullable]]*/, const sockaddr_any& serv_addr) { // IMPORTANT! @@ -3773,7 +3773,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(response, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (request)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3976,7 +3976,7 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas } EConnectStatus srt::CUDT::processRendezvous( - const CPacket& response, const sockaddr_any& serv_addr, + const CPacket* pResponse /*[[nullable]]*/, const sockaddr_any& serv_addr, EReadStatus rst, CPacket& w_reqpkt) { if (m_RdvState == CHandShake::RDV_CONNECTED) @@ -4053,7 +4053,7 @@ EConnectStatus srt::CUDT::processRendezvous( // We have JUST RECEIVED packet in this session (not that this is called as periodic update). // Sanity check m_tsLastReqTime = steady_clock::time_point(); - if (response.getLength() == size_t(-1)) + if (!pResponse || pResponse->getLength() == size_t(-1)) { m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Fatal, @@ -4061,7 +4061,7 @@ EConnectStatus srt::CUDT::processRendezvous( return CONN_REJECT; } - if (!interpretSrtHandshake(m_ConnRes, response, kmdata, &kmdatasize)) + if (!interpretSrtHandshake(m_ConnRes, *pResponse, kmdata, &kmdatasize)) { HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW."); @@ -4114,7 +4114,7 @@ EConnectStatus srt::CUDT::processRendezvous( // not be done in case of rendezvous. The section in postConnect() is // predicted to run only in regular CALLER handling. - if (rst != RST_OK || response.getLength() == size_t(-1)) + if (rst != RST_OK || !pResponse || pResponse->getLength() == size_t(-1)) { // Actually the -1 length would be an IPE, but it's likely that this was reported already. HLOGC( @@ -4125,7 +4125,7 @@ EConnectStatus srt::CUDT::processRendezvous( { HLOGC(cnlog.Debug, log << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension"); - if (!interpretSrtHandshake(m_ConnRes, response, 0, 0)) + if (!interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0)) { // m_RejectReason is already set, so set the reqtype accordingly m_ConnReq.m_iReqType = URQFailure(m_RejectReason); @@ -4165,10 +4165,7 @@ EConnectStatus srt::CUDT::processRendezvous( w_reqpkt.setLength(m_iMaxSRTPayloadSize); if (m_RdvState == CHandShake::RDV_CONNECTED) { - // When synchro=false, don't lock a mutex for rendezvous queue. - // This is required when this function is called in the - // receive queue worker thread - it would lock itself. - int cst = postConnect(response, true, 0); + int cst = postConnect(pResponse, true, 0); if (cst == CONN_REJECT) { // m_RejectReason already set @@ -4304,7 +4301,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx m_RdvState = CHandShake::RDV_CONNECTED; } - return postConnect(response, hsv5, eout); + return postConnect(&response, hsv5, eout); } if (!response.isControl(UMSG_HANDSHAKE)) @@ -4474,7 +4471,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx } } - return postConnect(response, false, eout); + return postConnect(&response, false, eout); } bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT @@ -4507,7 +4504,7 @@ bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT return true; } -EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT { if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. @@ -4516,6 +4513,21 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // in rendezvous it's completed before calling this function. if (!rendezvous) { + // The "local storage depleted" case shouldn't happen here, but + // this is a theoretical path that needs prevention. + bool ok = pResponse; + if (!ok) + { + m_RejectReason = SRT_REJ_IPE; + if (eout) + { + *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + return CONN_REJECT; + } + + // [[assert (pResponse != NULL)]]; + // NOTE: THIS function must be called before calling prepareConnectionObjects. // The reason why it's not part of prepareConnectionObjects is that the activities // done there are done SIMILAR way in acceptAndRespond, which also calls this @@ -4527,7 +4539,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // // Currently just this function must be called always BEFORE prepareConnectionObjects // everywhere except acceptAndRespond(). - bool ok = applyResponseSettings(); + ok = applyResponseSettings(); // This will actually be done also in rendezvous HSv4, // however in this case the HSREQ extension will not be attached, @@ -4537,8 +4549,8 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // May happen that 'response' contains a data packet that was sent in rendezvous mode. // In this situation the interpretation of handshake was already done earlier. - ok = ok && response.isControl(); - ok = ok && interpretSrtHandshake(m_ConnRes, response, 0, 0); + ok = ok && pResponse->isControl(); + ok = ok && interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0); if (!ok) { diff --git a/srtcore/core.h b/srtcore/core.h index 2e463423c..eed601ea0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -475,12 +475,12 @@ class CUDT /// @param response incoming handshake response packet to be interpreted /// @param serv_addr incoming packet's address /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) - SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket &response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); - SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; - SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr); + SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr); SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index fe323ab4c..9be4af013 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -903,17 +903,21 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& return NULL; } -void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) +void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, CUnit* unit) { vector toRemove, toProcess; + const CPacket* pkt = unit ? &unit->m_Packet : NULL; + + // 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; + // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. - if (!qualifyToHandle(rst, cst, pktIn.m_iID, toRemove, toProcess)) + if (!qualifyToHandle(rst, cst, dest_id, (toRemove), (toProcess))) return; - // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " << toRemove.size() << " to close"); @@ -938,7 +942,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (i->id != pktIn.m_iID) + if (i->id != dest_id) { read_st = RST_AGAIN; conn_st = CONN_AGAIN; @@ -947,7 +951,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); - if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) + if (!i->u->processAsyncConnectRequest(read_st, conn_st, pkt, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. @@ -1310,7 +1314,7 @@ void* srt::CRcvQueue::worker(void* param) // worker_TryAsyncRend_OrStore ---> // CUDT::processAsyncConnectResponse ---> // CUDT::processConnectResponse - self->m_pRendezvousQueue->updateConnStatus(rst, cst, unit->m_Packet); + self->m_pRendezvousQueue->updateConnStatus(rst, cst, unit); // XXX updateConnStatus may have removed the connector from the list, // however there's still m_mBuffer in CRcvQueue for that socket to care about. @@ -1526,7 +1530,7 @@ EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* un { LOGC(cnlog.Warn, log << "AsyncOrRND: PACKET NOT HANDSHAKE - re-requesting handshake from peer"); storePkt(id, unit->m_Packet.clone()); - if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, unit->m_Packet, u->m_PeerAddr)) + if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, &unit->m_Packet, u->m_PeerAddr)) { // Reuse previous behavior to reject a packet cst = CONN_REJECT; diff --git a/srtcore/queue.h b/srtcore/queue.h index b2ccb36d8..56afff9b6 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -354,7 +354,7 @@ class CRendezvousQueue /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. /// @param pktIn packet received from the UDP socket. - void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn); + void updateConnStatus(EReadStatus rst, EConnectStatus cst, CUnit* unit); private: struct LinkStatusInfo From e37f4abc017f717220ccb624db41aa0fa03e2349 Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Tue, 1 Jun 2021 18:43:22 +1000 Subject: [PATCH 191/790] [build] Set openssl vars explicitly; Support mbedtls Android build (#2030) --- scripts/build-android/build-android | 48 ++++++++++++++++++++++------- scripts/build-android/mkmbedtls | 27 ++++++++++++++++ scripts/build-android/mksrt | 11 +++++-- 3 files changed, 73 insertions(+), 13 deletions(-) create mode 100755 scripts/build-android/mkmbedtls diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 622f525d2..3d69dac0c 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -6,9 +6,11 @@ echo_help() echo " -n Specify NDK root path for the build." echo " -a Select target API level." echo " -t Select target architectures." - echo " Android supports the following architectures: armeabi armeabi-v7a arm64-v8a x86 x86_64." + echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64." + echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" - echo " -s Select a specific SRT tag. E.g. v1.4.3" + echo " -m Select Mbed TLS version. E.g. v2.26.0" + echo " -s Select SRT version. E.g. v1.4.3" echo echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" echo @@ -17,11 +19,13 @@ echo_help() # Init optional command line vars NDK_ROOT="" API_LEVEL=28 -BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" +BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" OPENSSL_VERSION=1.1.1h SRT_VERSION="" +ENC_LIB=openssl +MBEDTLS_VERSION=v2.26.0 -while getopts n:a:t:o:s: option +while getopts n:a:t:o:s:e:m: option do case "${option}" in @@ -30,6 +34,8 @@ do t) BUILD_TARGETS=${OPTARG};; o) OPENSSL_VERSION=${OPTARG};; s) SRT_VERSION=${OPTARG};; + e) ENC_LIB=${OPTARG};; + m) MBEDTLS_VERSION=${OPTARG};; *) twentytwo=${OPTARG};; esac done @@ -49,7 +55,19 @@ fi # Determine the path of the executing script BASE_DIR=$(readlink -f $0 | xargs dirname) -$BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION +if [ $ENC_LIB = 'openssl' ]; then + $BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION +elif [ $ENC_LIB = 'mbedtls' ]; then + if [ ! -d $BASE_DIR/mbedtls ]; then + git clone https://github.com/ARMmbed/mbedtls mbedtls + if [ ! -z "$MBEDTLS_VERSION" ]; then + git -C $BASE_DIR/mbedtls checkout $MBEDTLS_VERSION + fi + fi +else + echo "Unknown encryption library. Possible options: openssl mbedtls" + exit 128 +fi if [ ! -d $BASE_DIR/srt ]; then git clone https://github.com/Haivision/srt srt @@ -58,12 +76,20 @@ if [ ! -d $BASE_DIR/srt ]; then fi fi -JNI_DIR=$BASE_DIR/prebuilt - for build_target in $BUILD_TARGETS; do - git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/srt -i $BASE_DIR/$build_target + LIB_DIR=$BASE_DIR/$build_target/lib + JNI_DIR=$BASE_DIR/prebuilt/$build_target + + mkdir -p $JNI_DIR - mkdir -p $JNI_DIR/$build_target - cp $BASE_DIR/$build_target/lib/libsrt.so $JNI_DIR/$build_target/libsrt.so + if [ $ENC_LIB = 'mbedtls' ]; then + $BASE_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target + cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so + cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so + cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so + fi + + git -C $BASE_DIR/srt clean -fd + $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done diff --git a/scripts/build-android/mkmbedtls b/scripts/build-android/mkmbedtls new file mode 100755 index 000000000..176154184 --- /dev/null +++ b/scripts/build-android/mkmbedtls @@ -0,0 +1,27 @@ +#!/bin/sh + +while getopts s:i:t:n:a: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + + +BUILD_DIR=/tmp/mbedtls_android_build +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cd $BUILD_DIR +cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On \ +-DCMAKE_PREFIX_PATH=$INSTALL_DIR -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCMAKE_ANDROID_NDK=$NDK_ROOT \ +-DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$API_LEVEL -DCMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +-DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +-DCMAKE_BUILD_TYPE=RelWithDebInfo $SRC_DIR +cmake --build . +cmake --install . diff --git a/scripts/build-android/mksrt b/scripts/build-android/mksrt index 2af96a7aa..b2e176046 100755 --- a/scripts/build-android/mksrt +++ b/scripts/build-android/mksrt @@ -1,6 +1,6 @@ #!/bin/sh -while getopts s:i:t:n:a: option +while getopts s:i:t:n:a:e: option do case "${option}" in @@ -9,13 +9,20 @@ do t) ARCH_ABI=${OPTARG};; n) NDK_ROOT=${OPTARG};; a) API_LEVEL=${OPTARG};; + e) ENC_LIB=${OPTARG};; *) twentytwo=${OPTARG};; esac done cd $SRC_DIR -./configure --use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=true \ +./configure --use-enclib=$ENC_LIB \ +--use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=TRUE \ +--OPENSSL_INCLUDE_DIR=$INSTALL_DIR/include \ +--OPENSSL_CRYPTO_LIBRARY=$INSTALL_DIR/lib/libcrypto.a --OPENSSL_SSL_LIBRARY=$INSTALL_DIR/lib/libssl.a \ +--STATIC_MBEDTLS=FALSE \ +--MBEDTLS_INCLUDE_DIR=$INSTALL_DIR/include --MBEDTLS_INCLUDE_DIRS=$INSTALL_DIR/include \ +--MBEDTLS_LIBRARIES=$INSTALL_DIR/lib/libmbedtls.so \ --CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \ --CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ --CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ From 0f0caf948733033915f09914c907fe5e1ecc375b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 1 Jun 2021 15:08:17 +0200 Subject: [PATCH 192/790] [core] Added atomic support and marked atomic key fields detected by thread sanitizer (#1863) --- srtcore/api.cpp | 45 +++++--- srtcore/api.h | 4 +- srtcore/atomic.h | 210 ++++++++++++++++++++++++++++++++++++ srtcore/atomic_msvc.h | 245 ++++++++++++++++++++++++++++++++++++++++++ srtcore/congctl.cpp | 4 +- srtcore/core.cpp | 114 +++++++++++--------- srtcore/core.h | 75 ++++++------- srtcore/group.h | 2 +- srtcore/queue.cpp | 6 +- srtcore/queue.h | 10 +- srtcore/sync.h | 74 +++++++++++++ 11 files changed, 677 insertions(+), 112 deletions(-) create mode 100644 srtcore/atomic.h create mode 100644 srtcore/atomic_msvc.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ad243d50e..aee13f389 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2641,13 +2641,17 @@ void srt::CUDTUnited::checkBrokenSockets() // 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->m_pUDT->m_pRcvBuffer->isRcvDataAvailable() - && (s->m_pUDT->m_iBrokenCounter -- > 0)) + && s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable()) { - // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): - // %d\n", i->first); - // if there is still data in the receiver buffer, wait longer - continue; + const int bc = s->m_pUDT->m_iBrokenCounter.load(); + if (bc > 0) + { + // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): + // %d\n", i->first); + // if there is still data in the receiver buffer, wait longer + s->m_pUDT->m_iBrokenCounter.store(bc - 1); + continue; + } } #if ENABLE_EXPERIMENTAL_BONDING @@ -2702,15 +2706,17 @@ void srt::CUDTUnited::checkBrokenSockets() // RcvUList const steady_clock::time_point now = steady_clock::now(); const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; - if ((closed_ago > seconds_from(1)) - && ((!j->second->m_pUDT->m_pRNode) - || !j->second->m_pUDT->m_pRNode->m_bOnList)) + if (closed_ago > seconds_from(1)) { - HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " - << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); + CRNode* rnode = j->second->m_pUDT->m_pRNode; + if (!rnode || !rnode->m_bOnList) + { + HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " + << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); - // HLOGF(smlog.Debug, "will unref socket: %d\n", j->first); - tbr.push_back(j->first); + // HLOGF(smlog.Debug, "will unref socket: %d\n", j->first); + tbr.push_back(j->first); + } } } @@ -2734,6 +2740,19 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* const s = i->second; + // The socket may be in the trashcan now, but could + // still be under processing in the sender/receiver worker + // threads. If that's the case, SKIP IT THIS TIME. The + // socket will be checked next time the GC rollover starts. + CSNode* sn = s->m_pUDT->m_pSNode; + if (sn && sn->m_iHeapLoc != -1) + return; + + CRNode* rn = s->m_pUDT->m_pRNode; + if (rn && rn->m_bOnList) + return; + + #if ENABLE_EXPERIMENTAL_BONDING if (s->m_GroupOf) { diff --git a/srtcore/api.h b/srtcore/api.h index 4228d8b46..5750c9db1 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -102,7 +102,7 @@ class CUDTSocket void construct(); - SRT_SOCKSTATUS m_Status; //< current socket state + srt::sync::atomic m_Status; //< current socket state /// Time when the socket is closed. /// When the socket is closed, it is not removed immediately from the list @@ -421,7 +421,7 @@ friend class CRendezvousQueue; CCache* m_pCache; // UDT network information cache private: - volatile bool m_bClosing; + srt::sync::atomic m_bClosing; sync::Mutex m_GCStopLock; sync::Condition m_GCStopCond; diff --git a/srtcore/atomic.h b/srtcore/atomic.h new file mode 100644 index 000000000..5cedd396d --- /dev/null +++ b/srtcore/atomic.h @@ -0,0 +1,210 @@ +//---------------------------------------------------------------------------- +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or distribute +// this software, either in source code form or as a compiled binary, for any +// purpose, commercial or non-commercial, and by any means. +// +// In jurisdictions that recognize copyright laws, the author or authors of +// this software dedicate any and all copyright interest in the software to the +// public domain. We make this dedication for the benefit of the public at +// large and to the detriment of our heirs and successors. We intend this +// dedication to be an overt act of relinquishment in perpetuity of all present +// and future rights to this software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +//----------------------------------------------------------------------------- + +// SRT Project information: +// This file was adopted from a Public Domain project from +// https://github.com/mbitsnbites/atomic +// Only namespaces were changed to adopt it for SRT project. + +#ifndef SRT_SYNC_ATOMIC_H_ +#define SRT_SYNC_ATOMIC_H_ + +// Macro for disallowing copying of an object. +#if __cplusplus >= 201103L +#define ATOMIC_DISALLOW_COPY(T) \ + T(const T&) = delete; \ + T& operator=(const T&) = delete; +#else +#define ATOMIC_DISALLOW_COPY(T) \ + T(const T&); \ + T& operator=(const T&); +#endif + +// A portable static assert. +#if __cplusplus >= 201103L +#define ATOMIC_STATIC_ASSERT(condition, message) \ + static_assert((condition), message) +#else +// Based on: http://stackoverflow.com/a/809465/5778708 +#define ATOMIC_STATIC_ASSERT(condition, message) \ + _impl_STATIC_ASSERT_LINE(condition, __LINE__) +#define _impl_PASTE(a, b) a##b +#ifdef __GNUC__ +#define _impl_UNUSED __attribute__((__unused__)) +#else +#define _impl_UNUSED +#endif +#define _impl_STATIC_ASSERT_LINE(condition, line) \ + typedef char _impl_PASTE( \ + STATIC_ASSERT_failed_, \ + line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED +#endif + +#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) +#define ATOMIC_USE_GCC_INTRINSICS +#elif defined(_MSC_VER) +#define ATOMIC_USE_MSVC_INTRINSICS +#include "atomic_msvc.h" +#elif __cplusplus >= 201103L +#define ATOMIC_USE_CPP11_ATOMIC +#include +#else +#error Unsupported compiler / system. +#endif + +namespace srt { +namespace sync { +template +class atomic { +public: + ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || + sizeof(T) == 8, + "Only types of size 1, 2, 4 or 8 are supported"); + + atomic() : value_(static_cast(0)) {} + + explicit atomic(const T value) : value_(value) {} + + /// @brief Performs an atomic increment operation (value + 1). + /// @returns The new value of the atomic object. + T operator++() { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::increment(&value_); +#else + return ++value_; +#endif + } + + /// @brief Performs an atomic decrement operation (value - 1). + /// @returns The new value of the atomic object. + T operator--() { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::decrement(&value_); +#else + return --value_; +#endif + } + + /// @brief Performs an atomic compare-and-swap (CAS) operation. + /// + /// The value of the atomic object is only updated to the new value if the + /// old value of the atomic object matches @c expected_val. + /// + /// @param expected_val The expected value of the atomic object. + /// @param new_val The new value to write to the atomic object. + /// @returns True if new_value was written to the atomic object. + bool compare_exchange(const T expected_val, const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + T e = expected_val; + return __atomic_compare_exchange_n( + &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + const T old_val = + msvc::interlocked::compare_exchange(&value_, new_val, expected_val); + return (old_val == expected_val); +#else + T e = expected_val; + return value_.compare_exchange_weak(e, new_val); +#endif + } + + /// @brief Performs an atomic set operation. + /// + /// The value of the atomic object is unconditionally updated to the new + /// value. + /// + /// @param new_val The new value to write to the atomic object. + void store(const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + (void)msvc::interlocked::exchange(&value_, new_val); +#else + value_.store(new_val); +#endif + } + + /// @returns the current value of the atomic object. + /// @note Be careful about how this is used, since any operations on the + /// returned value are inherently non-atomic. + T load() const { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + // TODO(m): Is there a better solution for MSVC? + return value_; +#else + return value_; +#endif + } + + /// @brief Performs an atomic exchange operation. + /// + /// The value of the atomic object is unconditionally updated to the new + /// value, and the old value is returned. + /// + /// @param new_val The new value to write to the atomic object. + /// @returns the old value. + T exchange(const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::exchange(&value_, new_val); +#else + return value_.exchange(new_val); +#endif + } + + T operator=(const T new_value) { + store(new_value); + return new_value; + } + + operator T() const { + return load(); + } + +private: +#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) + volatile T value_; +#else + std::atomic value_; +#endif + + ATOMIC_DISALLOW_COPY(atomic) +}; + +} // namespace sync +} // namespace srt + +// Undef temporary defines. +#undef ATOMIC_USE_GCC_INTRINSICS +#undef ATOMIC_USE_MSVC_INTRINSICS +#undef ATOMIC_USE_CPP11_ATOMIC + +#endif // ATOMIC_ATOMIC_H_ diff --git a/srtcore/atomic_msvc.h b/srtcore/atomic_msvc.h new file mode 100644 index 000000000..9e4df2dbd --- /dev/null +++ b/srtcore/atomic_msvc.h @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or distribute +// this software, either in source code form or as a compiled binary, for any +// purpose, commercial or non-commercial, and by any means. +// +// In jurisdictions that recognize copyright laws, the author or authors of +// this software dedicate any and all copyright interest in the software to the +// public domain. We make this dedication for the benefit of the public at +// large and to the detriment of our heirs and successors. We intend this +// dedication to be an overt act of relinquishment in perpetuity of all present +// and future rights to this software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +//----------------------------------------------------------------------------- + +// SRT Project information: +// This file was adopted from a Public Domain project from +// https://github.com/mbitsnbites/atomic +// Only namespaces were changed to adopt it for SRT project. + +#ifndef SRT_SYNC_ATOMIC_MSVC_H_ +#define SRT_SYNC_ATOMIC_MSVC_H_ + +// Define which functions we need (don't include ). +extern "C" { +short _InterlockedIncrement16(short volatile*); +long _InterlockedIncrement(long volatile*); +__int64 _InterlockedIncrement64(__int64 volatile*); + +short _InterlockedDecrement16(short volatile*); +long _InterlockedDecrement(long volatile*); +__int64 _InterlockedDecrement64(__int64 volatile*); + +char _InterlockedExchange8(char volatile*, char); +short _InterlockedExchange16(short volatile*, short); +long __cdecl _InterlockedExchange(long volatile*, long); +__int64 _InterlockedExchange64(__int64 volatile*, __int64); + +char _InterlockedCompareExchange8(char volatile*, char, char); +short _InterlockedCompareExchange16(short volatile*, short, short); +long __cdecl _InterlockedCompareExchange(long volatile*, long, long); +__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64); +}; + +// Define which functions we want to use as inline intriniscs. +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedIncrement16) + +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedDecrement16) + +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedCompareExchange8) +#pragma intrinsic(_InterlockedCompareExchange16) + +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedExchange8) +#pragma intrinsic(_InterlockedExchange16) + +#if defined(_M_X64) +#pragma intrinsic(_InterlockedIncrement64) +#pragma intrinsic(_InterlockedDecrement64) +#pragma intrinsic(_InterlockedCompareExchange64) +#pragma intrinsic(_InterlockedExchange64) +#endif // _M_X64 + +namespace srt { +namespace sync { +namespace msvc { +template +struct interlocked { +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + // There's no _InterlockedIncrement8(). + char old_val, new_val; + do { + old_val = static_cast(*x); + new_val = old_val + static_cast(1); + } while (_InterlockedCompareExchange8(reinterpret_cast(x), + new_val, + old_val) != old_val); + return static_cast(new_val); + } + + static inline T decrement(T volatile* x) { + // There's no _InterlockedDecrement8(). + char old_val, new_val; + do { + old_val = static_cast(*x); + new_val = old_val - static_cast(1); + } while (_InterlockedCompareExchange8(reinterpret_cast(x), + new_val, + old_val) != old_val); + return static_cast(new_val); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange8(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast(_InterlockedExchange8( + reinterpret_cast(x), static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + return static_cast( + _InterlockedIncrement16(reinterpret_cast(x))); + } + + static inline T decrement(T volatile* x) { + return static_cast( + _InterlockedDecrement16(reinterpret_cast(x))); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange16(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast( + _InterlockedExchange16(reinterpret_cast(x), + static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + return static_cast( + _InterlockedIncrement(reinterpret_cast(x))); + } + + static inline T decrement(T volatile* x) { + return static_cast( + _InterlockedDecrement(reinterpret_cast(x))); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast(_InterlockedExchange( + reinterpret_cast(x), static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { +#if defined(_M_X64) + return static_cast( + _InterlockedIncrement64(reinterpret_cast(x))); +#else + // There's no _InterlockedIncrement64() for 32-bit x86. + __int64 old_val, new_val; + do { + old_val = static_cast<__int64>(*x); + new_val = old_val + static_cast<__int64>(1); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(new_val); +#endif // _M_X64 + } + + static inline T decrement(T volatile* x) { +#if defined(_M_X64) + return static_cast( + _InterlockedDecrement64(reinterpret_cast(x))); +#else + // There's no _InterlockedDecrement64() for 32-bit x86. + __int64 old_val, new_val; + do { + old_val = static_cast<__int64>(*x); + new_val = old_val - static_cast<__int64>(1); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(new_val); +#endif // _M_X64 + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast(_InterlockedCompareExchange64( + reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { +#if defined(_M_X64) + return static_cast( + _InterlockedExchange64(reinterpret_cast(x), + static_cast(new_val))); +#else + // There's no _InterlockedExchange64 for 32-bit x86. + __int64 old_val; + do { + old_val = static_cast<__int64>(*x); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(old_val); +#endif // _M_X64 + } +}; +} // namespace msvc +} // namespace sync +} // namespace srt + +#endif // ATOMIC_ATOMIC_MSVC_H_ diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 998256216..4612e6852 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -60,7 +60,7 @@ void SrtCongestion::Check() class LiveCC: public SrtCongestionControlBase { int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) - size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit + srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; // NAKREPORT stuff. @@ -167,7 +167,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod << " by avg pktsize=" << m_zSndAvgPayloadSize diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 90162fa89..17611191e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -254,7 +254,7 @@ void srt::CUDT::construct() // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; - m_tsLastReqTime = steady_clock::time_point(); + m_tsLastReqTime.store(steady_clock::time_point()); m_SrtHsSide = HSD_DRAW; m_uPeerSrtVersion = 0; // Not defined until connected. m_iTsbPdDelay_ms = 0; @@ -947,11 +947,11 @@ void srt::CUDT::open() m_tdNAKInterval = m_tdMinNakInterval; const steady_clock::time_point currtime = steady_clock::now(); - m_tsLastRspTime = currtime; - m_tsNextACKTime = currtime + m_tdACKInterval; - m_tsNextNAKTime = currtime + m_tdNAKInterval; - m_tsLastRspAckTime = currtime; - m_tsLastSndTime = currtime; + m_tsLastRspTime.store(currtime); + m_tsNextACKTime.store(currtime + m_tdACKInterval); + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); + m_tsLastRspAckTime = currtime; + m_tsLastSndTime.store(currtime); m_tsUnstableSince = steady_clock::time_point(); m_tsFreshActivation = steady_clock::time_point(); @@ -3524,7 +3524,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) while (!m_bClosing) { - const steady_clock::duration tdiff = steady_clock::now() - m_tsLastReqTime; + const steady_clock::duration tdiff = steady_clock::now() - m_tsLastReqTime.load(); // avoid sending too many requests, at most 1 request per 250ms // SHORT VERSION: @@ -5714,11 +5714,11 @@ SRT_REJECT_REASON srt::CUDT::setupCC() // Update timers const steady_clock::time_point currtime = steady_clock::now(); - m_tsLastRspTime = currtime; - m_tsNextACKTime = currtime + m_tdACKInterval; - m_tsNextNAKTime = currtime + m_tdNAKInterval; - m_tsLastRspAckTime = currtime; - m_tsLastSndTime = currtime; + m_tsLastRspTime.store(currtime); + m_tsNextACKTime.store(currtime + m_tdACKInterval); + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); + m_tsLastRspAckTime = currtime; + m_tsLastSndTime.store(currtime); HLOGC(rslog.Debug, log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize @@ -6848,6 +6848,7 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout + // XXX Lock ??? ScopedLock ack_lock(m_RecvAckLock); m_tsLastRspAckTime = steady_clock::now(); m_iReXmitCount = 1; } @@ -7151,8 +7152,8 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; - perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval); - perf->pktFlowWindow = m_iFlowWindowSize; + perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); + perf->pktFlowWindow = m_iFlowWindowSize.load(); perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); perf->msRTT = (double)m_iSRTT / 1000.0; @@ -7164,7 +7165,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; - const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth; + const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth.load(); perf->mbpsBandwidth = Bps2Mbps(availbw * (m_iMaxSRTPayloadSize + pktHdrSize)); @@ -7640,7 +7641,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp // Fix keepalive if (nbsent) - m_tsLastSndTime = steady_clock::now(); + m_tsLastSndTime.store(steady_clock::now()); } int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) @@ -8010,7 +8011,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) { ScopedLock ack_lock(m_RecvAckLock); - m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); + m_iFlowWindowSize = m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); m_iSndLastAck = ackdata_seqno; // TODO: m_tsLastRspAckTime should be protected with m_RecvAckLock @@ -8150,11 +8151,15 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + int crtt = m_iSRTT.load(), crttvar = m_iRTTVar.load(); + + if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); - m_iSRTT = avg_iir<8>(m_iSRTT, rtt); + crttvar = avg_iir<4>(crttvar, abs(crtt - crtt)); + crtt = avg_iir<8>(crtt, crtt); } + m_iSRTT = crtt; + m_iRTTVar = crttvar; } else // Transmission is unidirectional. { @@ -8212,9 +8217,9 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ else bytesps = pktps * m_iMaxSRTPayloadSize; - m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth); - m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps); - m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps); + m_iBandwidth = avg_iir<8>(m_iBandwidth.load(), bandwidth); + m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate.load(), pktps); + m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate.load(), bytesps); // Update Estimated Bandwidth and packet delivery rate // m_iRcvRate = m_iDeliveryRate; @@ -8272,8 +8277,8 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // on subsequent RTT samples (during transmission). if (m_bIsFirstRTTReceived) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); - m_iSRTT = avg_iir<8>(m_iSRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar.load(), abs(rtt - m_iSRTT.load())); + m_iSRTT = avg_iir<8>(m_iSRTT.load(), rtt); } // Reset the value of smoothed RTT on the first RTT sample after initialization // (at the beginning of transmission). @@ -8568,7 +8573,7 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); if (nbsent) { - m_tsLastSndTime = steady_clock::now(); + m_tsLastSndTime.store(steady_clock::now()); } } } @@ -8687,7 +8692,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_CGWARNING: // 100 - Delay Warning // One way packet delay is increasing, so decrease the sending rate - m_tdSendInterval = (m_tdSendInterval * 1125) / 1000; + m_tdSendInterval = (m_tdSendInterval.load() * 1125) / 1000; // XXX Note as interesting fact: this is only prepared for handling, // but nothing in the code is sending this message. Probably predicted // for a custom congctl. There's a predicted place to call it under @@ -8957,7 +8962,9 @@ std::pair srt::CUDT::packData(CPacket& w_packet) const steady_clock::time_point enter_time = steady_clock::now(); if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime) - m_tdSendTimeDiff += enter_time - m_tsNextSendTime; + { + m_tdSendTimeDiff = m_tdSendTimeDiff.load() + (enter_time - m_tsNextSendTime); + } string reason = "reXmit"; @@ -9083,7 +9090,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) else { m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tdSendTimeDiff = steady_clock::duration(); return std::make_pair(0, enter_time); } } @@ -9092,7 +9099,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tdSendTimeDiff = steady_clock::duration(); return std::make_pair(0, enter_time); } @@ -9166,7 +9173,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #endif // Fix keepalive - m_tsLastSndTime = enter_time; + m_tsLastSndTime.store(enter_time); considerLegacySrtHandshake(steady_clock::time_point()); @@ -9205,18 +9212,24 @@ std::pair srt::CUDT::packData(CPacket& w_packet) else { #if USE_BUSY_WAITING - m_tsNextSendTime = enter_time + m_tdSendInterval; + m_tsNextSendTime = enter_time + m_tdSendInterval.load(); #else - if (m_tdSendTimeDiff >= m_tdSendInterval) + const duration sendint = m_tdSendInterval; + const duration sendbrw = m_tdSendTimeDiff; + + if (sendbrw >= sendint) { // Send immidiately m_tsNextSendTime = enter_time; - m_tdSendTimeDiff -= m_tdSendInterval; + + // ATOMIC NOTE: this is the only thread that + // modifies this field + m_tdSendTimeDiff = sendbrw - sendint; } else { - m_tsNextSendTime = enter_time + (m_tdSendInterval - m_tdSendTimeDiff); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tsNextSendTime = enter_time + (sendint - sendbrw); + m_tdSendTimeDiff = duration(); } #endif } @@ -9343,7 +9356,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; - m_tsLastRspTime = steady_clock::now(); + m_tsLastRspTime.store(steady_clock::now()); const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; @@ -9792,7 +9805,7 @@ int srt::CUDT::processData(CUnit* in_unit) // a given period). if (m_CongCtl->needsQuickACK(packet)) { - m_tsNextACKTime = steady_clock::now(); + m_tsNextACKTime.store(steady_clock::now()); } } @@ -10626,7 +10639,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); result = -1; // enforce fallthrough for the below condition! - hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? SRT_REJ_IPE : m_RejectReason); + hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? int(SRT_REJ_IPE) : m_RejectReason.load()); } else { @@ -10698,7 +10711,7 @@ void srt::CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) { int because_decision = BECAUSE_NO_REASON; - if (currtime > m_tsNextACKTime // ACK time has come + if (currtime > m_tsNextACKTime.load() // ACK time has come // OR the number of sent packets since last ACK has reached // the congctl-defined value of ACK Interval // (note that none of the builtin congctls defines ACK Interval) @@ -10710,7 +10723,7 @@ int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) const steady_clock::duration ack_interval = m_CongCtl->ACKTimeout_us() > 0 ? microseconds_from(m_CongCtl->ACKTimeout_us()) : m_tdACKInterval; - m_tsNextACKTime = currtime + ack_interval; + m_tsNextACKTime.store(currtime + ack_interval); m_iPktCount = 0; m_iLightACKCount = 1; @@ -10762,14 +10775,14 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) if (loss_len > 0) { - if (currtime <= m_tsNextNAKTime) + if (currtime <= m_tsNextNAKTime.load()) return BECAUSE_NO_REASON; // wait for next NAK time sendCtrl(UMSG_LOSSREPORT); debug_decision = BECAUSE_NAKREPORT; } - m_tsNextNAKTime = currtime + m_tdNAKInterval; + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); return debug_decision; } @@ -10805,7 +10818,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec steady_clock::time_point next_exp_time; if (m_CongCtl->RTO()) { - next_exp_time = m_tsLastRspTime + microseconds_from(m_CongCtl->RTO()); + next_exp_time = m_tsLastRspTime.load() + microseconds_from(m_CongCtl->RTO()); } else { @@ -10813,7 +10826,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec microseconds_from(m_iEXPCount * (m_iSRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); if (exp_timeout < (m_iEXPCount * m_tdMinExpInterval)) exp_timeout = m_iEXPCount * m_tdMinExpInterval; - next_exp_time = m_tsLastRspTime + exp_timeout; + next_exp_time = m_tsLastRspTime.load() + exp_timeout; } if (currtime <= next_exp_time && !m_bBreakAsUnstable) @@ -10823,8 +10836,9 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds + time_point last_rsp_time = m_tsLastRspTime.load(); if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && - (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US)))) + (currtime - last_rsp_time > microseconds_from(PEER_IDLE_TMO_US)))) { // // Connection is broken. @@ -10832,7 +10846,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec // Application will detect this when it calls any UDT methods next time. // HLOGC(xtlog.Debug, - log << "CONNECTION EXPIRED after " << count_milliseconds(currtime - m_tsLastRspTime) << "ms"); + log << "CONNECTION EXPIRED after " << count_milliseconds(currtime - last_rsp_time) << "ms"); m_bClosing = true; m_bBroken = true; m_iBrokenCounter = 30; @@ -10848,7 +10862,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec HLOGC(xtlog.Debug, log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) << " elapsed=" - << (count_microseconds(currtime - m_tsLastRspTime)) << "/" << (+PEER_IDLE_TMO_US) << "us"); + << (count_microseconds(currtime - last_rsp_time)) << "/" << (+PEER_IDLE_TMO_US) << "us"); ++m_iEXPCount; @@ -10969,7 +10983,7 @@ void srt::CUDT::checkTimers() // Check if FAST or LATE packet retransmission is required checkRexmitTimer(currtime); - if (currtime > m_tsLastSndTime + microseconds_from(COMM_KEEPALIVE_PERIOD_US)) + if (currtime > m_tsLastSndTime.load() + microseconds_from(COMM_KEEPALIVE_PERIOD_US)) { sendCtrl(UMSG_KEEPALIVE); #if ENABLE_EXPERIMENTAL_BONDING @@ -11051,7 +11065,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // explicitly, otherwise they will never be deleted. if (pending_broken) { - // XXX This somehow can cause a deadlock + // XXX This somehow can cause a deadlock, even without GlobControlLock // s_UDTUnited.close(m_parent); m_parent->setBrokenClosed(); } diff --git a/srtcore/core.h b/srtcore/core.h index eed601ea0..d190780a0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -56,7 +56,6 @@ modified by #include #include - #include "srt.h" #include "common.h" #include "list.h" @@ -173,6 +172,8 @@ class CUDT typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; + typedef srt::sync::AtomicClock atomic_time_point; + typedef srt::sync::AtomicDuration atomic_duration; private: // constructor and desctructor void construct(); @@ -310,7 +311,7 @@ class CUDT int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } + srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } @@ -723,30 +724,30 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - volatile bool m_bListening; // If the UDT entity is listening to connection - volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed - volatile bool m_bConnected; // Whether the connection is on or off - volatile bool m_bClosing; // If the UDT entity is closing - volatile bool m_bShutdown; // If the peer side has shutdown the connection - volatile bool m_bBroken; // If the connection has been broken - volatile bool m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. - volatile bool m_bPeerHealth; // If the peer status is normal - volatile int m_RejectReason; + srt::sync::atomic m_bListening; // If the UDT entity is listening to connection + srt::sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed + srt::sync::atomic m_bConnected; // Whether the connection is on or off + srt::sync::atomic m_bClosing; // If the UDT entity is closing + srt::sync::atomic m_bShutdown; // If the peer side has shutdown the connection + srt::sync::atomic m_bBroken; // If the connection has been broken + srt::sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. + srt::sync::atomic m_bPeerHealth; // If the peer status is normal + srt::sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - int m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + srt::sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter - int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + srt::sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second + srt::sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds - int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds - bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair + srt::sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + srt::sync::atomic m_bIsFirstRTTReceived;// True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. - int m_iDeliveryRate; // Packet arrival rate at the receiver side - int m_iByteDeliveryRate; // Byte arrival rate at the receiver side + srt::sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side + srt::sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response @@ -758,24 +759,24 @@ class CUDT CSndLossList* m_pSndLossList; // Sender loss list CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window - /*volatile*/ duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles + atomic_duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles - /*volatile*/ duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time + atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time - volatile int m_iFlowWindowSize; // Flow control window size - volatile double m_dCongestionWindow; // Congestion window size + srt::sync::atomic m_iFlowWindowSize; // Flow control window size + double m_dCongestionWindow; // Congestion window size private: // Timers - /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below - /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time - - /*volatile*/ duration m_tdACKInterval; // ACK interval - /*volatile*/ duration m_tdNAKInterval; // NAK interval - /*volatile*/ time_point m_tsLastRspTime; // Timestamp of last response from the peer - /*volatile*/ time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer - /*volatile*/ time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) + atomic_time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below + atomic_time_point m_tsNextNAKTime; // Next NAK time + + duration m_tdACKInterval; // ACK interval + duration m_tdNAKInterval; // NAK interval + atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer + time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer + atomic_time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) time_point m_tsLastWarningTime; // Last time that a warning message is sent - time_point m_tsLastReqTime; // last time when a connection request is sent + atomic_time_point m_tsLastReqTime; // last time when a connection request is sent time_point m_tsRcvPeerStartTime; time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) time_point m_tsLastAckTime; // Timestamp of last ACK @@ -787,8 +788,8 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending - volatile int32_t m_iSndLastFullAck; // Last full ACK received - volatile int32_t m_iSndLastAck; // Last ACK received + srt::sync::atomic m_iSndLastFullAck;// Last full ACK received + srt::sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) // and this is the sequence number that refers to the block at position [0]. Upon acknowledgement, @@ -798,9 +799,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list - volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - volatile int32_t m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + srt::sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list + srt::sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + srt::sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -862,7 +863,7 @@ class CUDT int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged int32_t m_iAckSeqNo; // Last ACK sequence number - int32_t m_iRcvCurrSeqNo; // Largest received sequence number + srt::sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) int32_t m_iPeerISN; // Initial Sequence Number of the peer side diff --git a/srtcore/group.h b/srtcore/group.h index ec5d124d3..549900370 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -435,7 +435,7 @@ class CUDTGroup bool m_bSyncOnMsgNo; SRT_GROUP_TYPE m_type; CUDTSocket* m_listener; // A "group" can only have one listener. - int m_iBusy; + srt::sync::atomic m_iBusy; CallbackHolder m_cbConnectHook; void installConnectHook(srt_connect_callback_fn* hook, void* opaq) { diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 9be4af013..b9d7ed02e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -242,15 +242,17 @@ void srt::CUnitQueue::makeUnitFree(CUnit* unit) SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); unit->m_iFlag = CUnit::FREE; + --m_iCount; } void srt::CUnitQueue::makeUnitGood(CUnit* unit) { + ++m_iCount; + SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); unit->m_iFlag = CUnit::GOOD; - ++m_iCount; } srt::CSndUList::CSndUList() @@ -431,7 +433,7 @@ void srt::CSndUList::remove_(const CUDT* u) // remove the node from heap m_pHeap[n->m_iHeapLoc] = m_pHeap[m_iLastEntry]; m_iLastEntry--; - m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc; + m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc.load(); int q = n->m_iHeapLoc; int p = q * 2 + 1; diff --git a/srtcore/queue.h b/srtcore/queue.h index 56afff9b6..ee05440c8 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -137,7 +137,7 @@ class CUnitQueue CUnit* m_pAvailUnit; // recent available unit int m_iSize; // total size of the unit queue, in number of packets - int m_iCount; // total number of valid (occupied) packets in the queue + srt::sync::atomic m_iCount; // total number of valid (occupied) packets in the queue int m_iMSS; // unit buffer size int m_iIPversion; // IP version @@ -152,7 +152,7 @@ struct CSNode CUDT* m_pUDT; // Pointer to the instance of CUDT socket sync::steady_clock::time_point m_tsTimeStamp; - int m_iHeapLoc; // location on the heap, -1 means not on the heap + srt::sync::atomic m_iHeapLoc; // location on the heap, -1 means not on the heap }; class CSndUList @@ -240,7 +240,7 @@ struct CRNode CRNode* m_pPrev; // previous link CRNode* m_pNext; // next link - bool m_bOnList; // if the node is already on the list + srt::sync::atomic m_bOnList; // if the node is already on the list }; class CRcvUList @@ -465,7 +465,7 @@ class CSndQueue srt::sync::Mutex m_WindowLock; srt::sync::Condition m_WindowCond; - volatile bool m_bClosing; // closing the worker + srt::sync::atomic m_bClosing; // closing the worker #if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker uint64_t m_ullDbgPeriod; @@ -545,7 +545,7 @@ class CRcvQueue size_t m_szPayloadSize; // packet payload size - volatile bool m_bClosing; // closing the worker + srt::sync::atomic m_bClosing; // closing the worker #if ENABLE_LOGGING static int m_counter; #endif diff --git a/srtcore/sync.h b/srtcore/sync.h index f25c5ca8d..53123c682 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -18,10 +18,12 @@ #include #include #include +#include #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_STDCXX_STEADY #define SRT_SYNC_CLOCK_STR "STDCXX_STEADY" #else #include +#include "atomic.h" // Defile clock type to use #ifdef IA32 @@ -180,6 +182,11 @@ class TimePoint { } + TimePoint(const Duration& duration_since_epoch) + : m_timestamp(duration_since_epoch.count()) + { + } + ~TimePoint() {} public: // Relational operators @@ -224,6 +231,73 @@ inline Duration operator*(const int& lhs, const Duration +class AtomicDuration +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicDuration() ATR_NOEXCEPT : dur(0) {} + + duration_type load() + { + int64_t val = dur.load(); + return duration_type(val); + } + + void store(const duration_type& d) + { + dur.store(d.count()); + } + + AtomicDuration& operator=(const duration_type& s) + { + dur = s.count(); + return *this; + } + + operator duration_type() const + { + return duration_type(dur); + } +}; + +template +class AtomicClock +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicClock() ATR_NOEXCEPT : dur(0) {} + + time_point_type load() const + { + int64_t val = dur.load(); + return time_point_type(duration_type(val)); + } + + void store(const time_point_type& d) + { + dur.store(uint64_t(d.time_since_epoch().count())); + } + + AtomicClock& operator=(const time_point_type& s) + { + dur = s.time_since_epoch().count(); + return *this; + } + + operator time_point_type() const + { + return time_point_type(duration_type(dur.load())); + } +}; + + /////////////////////////////////////////////////////////////////////////////// // // Duration and timepoint conversions From 28a7006a3a35ec9331f6c2c310e353e9ba2a6368 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 1 Jun 2021 21:31:09 +0800 Subject: [PATCH 193/790] [core] use seq larger than m_RcvBaseSeqNo to update group readablity (#2026) --- srtcore/buffer.cpp | 62 +++++++++++++++++++++++++++++++--------------- srtcore/buffer.h | 11 ++++++-- srtcore/core.cpp | 11 +++++++- srtcore/group.cpp | 6 +++++ srtcore/group.h | 1 + 5 files changed, 68 insertions(+), 23 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index db3102e7a..b63439580 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1123,8 +1123,10 @@ size_t CRcvBuffer::dropData(int len) bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, - int32_t& w_curpktseq) + int32_t& w_curpktseq, + int32_t base_seq) { + HLOGC(brlog.Debug, log << "getRcvFirstMsg: base_seq=" << base_seq); w_skipseqno = SRT_SEQNO_NONE; w_passack = false; // tsbpdtime will be retrieved by the below call @@ -1137,8 +1139,8 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, /* Check the acknowledged packets */ // getRcvReadyMsg returns true if the time to play for the first message - // (returned in w_tsbpdtime) is in the past. - if (getRcvReadyMsg((w_tsbpdtime), (w_curpktseq), -1)) + // that larger than base_seq is in the past. + if (getRcvReadyMsg((w_tsbpdtime), (w_curpktseq), -1, base_seq)) { HLOGC(brlog.Debug, log << "getRcvFirstMsg: ready CONTIG packet: %" << w_curpktseq); return true; @@ -1167,9 +1169,10 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, * No acked packets ready but caller want to know next packet to wait for * Check the not yet acked packets that may be stuck by missing packet(s). */ - bool haslost = false; - w_tsbpdtime = steady_clock::time_point(); // redundant, for clarity - w_passack = true; + bool haslost = false; + steady_clock::time_point tsbpdtime = steady_clock::time_point(); + w_tsbpdtime = steady_clock::time_point(); + w_passack = true; // XXX SUSPECTED ISSUE with this algorithm: // The above call to getRcvReadyMsg() should report as to whether: @@ -1195,8 +1198,11 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // When done so, the below loop would be completely unnecessary. // Logical description of the below algorithm: - // 1. Check if the VERY FIRST PACKET is valid; if so then: - // - check if it's ready to play, return boolean value that marks it. + // 1. update w_tsbpdtime and w_curpktseq if found one packet ready to play + // - keep check the next packet if still smaller than base_seq + // 2. set w_skipseqno if found packets before w_curpktseq lost + // if no packets larger than base_seq ready to play, return the largest RTP + // else return the first one that larger than base_seq and rady to play for (int i = m_iLastAckPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) { @@ -1208,19 +1214,21 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, } else { - /* We got the 1st valid packet */ - w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); - if (w_tsbpdtime <= steady_clock::now()) + tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + if (tsbpdtime <= steady_clock::now()) { /* Packet ready to play */ + w_tsbpdtime = tsbpdtime; + w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; if (haslost) + w_skipseqno = w_curpktseq; + + if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) { - /* - * Packet stuck on non-acked side because of missing packets. - * Tell 1st valid packet seqno so caller can skip (drop) the missing packets. - */ - w_skipseqno = m_pUnit[i]->m_Packet.m_iSeqNo; - w_curpktseq = w_skipseqno; + HLOGC(brlog.Debug, + log << "getRcvFirstMsg: found ready packet %" << w_curpktseq + << " but not larger than base_seq, try next"); + continue; } HLOGC(brlog.Debug, @@ -1234,6 +1242,10 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // ... return true; } + + if (!is_zero(w_tsbpdtime)) { + return true; + } HLOGC(brlog.Debug, log << "getRcvFirstMsg: found NOT READY packet, nSKIPPED: " << ((i - m_iLastAckPos + m_iSize) % m_iSize)); @@ -1246,6 +1258,9 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // the 'haslost' is set, which means that it continues only to find the first valid // packet after stating that the very first packet isn't valid. } + if (!is_zero(w_tsbpdtime)) { + return true; + } HLOGC(brlog.Debug, log << "getRcvFirstMsg: found NO PACKETS"); return false; } @@ -1276,7 +1291,7 @@ int32_t CRcvBuffer::getTopMsgno() const return m_pUnit[m_iStartPos]->m_Packet.getMsgSeq(); } -bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto) +bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq) { const bool havelimit = upto != -1; int end = -1, past_end = -1; @@ -1342,7 +1357,8 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& // 1. Get the TSBPD time of the unit. Stop and return false if this unit // is not yet ready to play. // 2. If it's ready to play, check also if it's decrypted. If not, skip it. - // 3. If it's ready to play and decrypted, stop and return it. + // 3. Check also if it's larger than base_seq, if not, skip it. + // 4. If it's ready to play, decrypted and larger than base, stop and return it. if (!havelimit) { w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); @@ -1361,6 +1377,12 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& IF_HEAVY_LOGGING(reason = "DECRYPTION FAILED"); freeunit = true; /* packet not decrypted */ } + else if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) + { + IF_HEAVY_LOGGING(reason = "smaller than base_seq"); + w_tsbpdtime = steady_clock::time_point(); + freeunit = true; + } else { HLOGC(brlog.Debug, @@ -1415,7 +1437,7 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& if (freeunit) { - HLOGC(brlog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED"); + HLOGC(brlog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED: " << reason); /* removed skipped, dropped, undecryptable bytes from rcv buffer */ const int rmbytes = (int)m_pUnit[i]->m_Packet.getLength(); countBytes(-1, -rmbytes, true); diff --git a/srtcore/buffer.h b/srtcore/buffer.h index f69bef1d9..9bf02f216 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -430,11 +430,17 @@ class CRcvBuffer /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by /// missing packets. + /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base if exist packet ready-to-play + /// and larger than base /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. - bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); + bool getRcvFirstMsg(time_point& w_tsbpdtime, + bool& w_passack, + int32_t& w_skipseqno, + int32_t& w_curpktseq, + int32_t base_seq = SRT_SEQNO_NONE); /// Update the ACK point of the buffer. /// @param [in] len size of data to be skip & acknowledged. @@ -473,9 +479,10 @@ class CRcvBuffer /// Parameters (of the 1st packet queue, ready to play or not): /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if /// none + /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base /// @retval true 1st packet ready to play without discontinuity (no hole) /// @retval false tsbpdtime = 0: no packet ready to play - bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); + bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq = SRT_SEQNO_NONE); public: /// @brief Get clock drift in microseconds. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 17611191e..7a337cf41 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5161,8 +5161,17 @@ void * srt::CUDT::tsbpd(void *param) int32_t current_pkt_seq = 0; steady_clock::time_point tsbpdtime; bool rxready = false; + int32_t rcv_base_seq = SRT_SEQNO_NONE; #if ENABLE_EXPERIMENTAL_BONDING bool shall_update_group = false; + if (gkeeper.group) + { + // Functions called below will lock m_GroupLock, which in hierarchy + // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock + // m_GroupLock inside the calls. + InvertedLock unrecv(self->m_RecvLock); + rcv_base_seq = gkeeper.group->getRcvBaseSeqNo(); + } #endif enterCS(self->m_RcvBufferLock); @@ -5174,7 +5183,7 @@ void * srt::CUDT::tsbpd(void *param) int32_t skiptoseqno = SRT_SEQNO_NONE; bool passack = true; // Get next packet to wait for even if not acked - rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq)); + rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq), rcv_base_seq); HLOGC(tslog.Debug, log << boolalpha << "NEXT PKT CHECK: rdy=" << rxready << " passack=" << passack << " skipto=%" diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 854508ef4..a0be7dd72 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2134,6 +2134,12 @@ void CUDTGroup::updateReadState(SRTSOCKET /* not sure if needed */, int32_t sequ } } +int32_t CUDTGroup::getRcvBaseSeqNo() +{ + ScopedLock lg(m_GroupLock); + return m_RcvBaseSeqNo; +} + void CUDTGroup::updateWriteState() { ScopedLock lg(m_GroupLock); diff --git a/srtcore/group.h b/srtcore/group.h index 549900370..e4b2fb6ed 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -345,6 +345,7 @@ class CUDTGroup void updateWriteState(); void updateFailedLink(); void activateUpdateEvent(bool still_have_items); + int32_t getRcvBaseSeqNo(); /// Update the in-group array of packet providers per sequence number. /// Also basing on the information already provided by possibly other sockets, From b4a5887964d1a56df57b7f45beb77c7e1fea51bd Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 2 Jun 2021 16:19:16 +0800 Subject: [PATCH 194/790] [build] Added compile_commands.json to .gitignore (#2031) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 10f985468..699d0e1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ _*/ # Ignore vcpkg submodule vcpkg/ + +# LSP +compile_commands.json From d6e8e213c0b374444bb5f0c04fda54197869b655 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 3 Jun 2021 11:15:12 +0800 Subject: [PATCH 195/790] [core] Fix build error when -DSRT_DEBUG_TRACE_DRIFT=1 --- srtcore/tsbpd_time.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 8fedf82ea..ff01f8a30 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -22,7 +22,7 @@ namespace srt #if SRT_DEBUG_TRACE_DRIFT class drift_logger { - using steady_clock = srt::sync::steady_clock; + typedef srt::sync::steady_clock steady_clock; public: drift_logger() {} @@ -33,12 +33,12 @@ class drift_logger m_fout.close(); } - void trace(unsigned ackack_timestamp, - int rtt_us, - int64_t drift_sample, - int64_t drift, - int64_t overdrift, - const std::chrono::steady_clock::time_point& tsbpd_base) + void trace(unsigned ackack_timestamp, + int rtt_us, + int64_t drift_sample, + int64_t drift, + int64_t overdrift, + const srt::sync::steady_clock::time_point& tsbpd_base) { using namespace srt::sync; ScopedLock lck(m_mtx); From acf38a72586cf50e94e6ff8c20344eb14067caa3 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Thu, 3 Jun 2021 16:04:06 +0800 Subject: [PATCH 196/790] [core] Fixed skip non-empty data (#2033) --- srtcore/buffer.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index b63439580..e2eb4f406 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1169,10 +1169,11 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, * No acked packets ready but caller want to know next packet to wait for * Check the not yet acked packets that may be stuck by missing packet(s). */ - bool haslost = false; - steady_clock::time_point tsbpdtime = steady_clock::time_point(); - w_tsbpdtime = steady_clock::time_point(); - w_passack = true; + bool haslost = false; + int last_ready_pos = -1; + steady_clock::time_point tsbpdtime = steady_clock::time_point(); + w_tsbpdtime = steady_clock::time_point(); + w_passack = true; // XXX SUSPECTED ISSUE with this algorithm: // The above call to getRcvReadyMsg() should report as to whether: @@ -1215,11 +1216,20 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, else { tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + /* Packet ready to play */ if (tsbpdtime <= steady_clock::now()) { - /* Packet ready to play */ - w_tsbpdtime = tsbpdtime; - w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; + // If the last ready-to-play packet exists, free it. + if (!is_zero(w_tsbpdtime)) { + HLOGC(brlog.Debug, + log << "getRcvFirstMsg: found next ready packet, free last %" + << w_curpktseq << " POS=" << last_ready_pos); + SRT_ASSERT(w_curpktseq != SRT_SEQNO_NONE); + freeUnitAt(last_ready_pos); + } + w_tsbpdtime = tsbpdtime; + w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; + last_ready_pos = i; if (haslost) w_skipseqno = w_curpktseq; From 5205c3cc0edeef78c3737f3c02eafad14fe1f122 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 4 Jun 2021 09:45:23 +0200 Subject: [PATCH 197/790] [docs] Edits of the requirements in ReadMe.md (#2035) --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c9aff2d8..e0403c4df 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,15 @@ As audio/video packets are streamed from a source to a destination device, SRT d ## Requirements -* cmake (as build system) -* Tcl 8.5 (optional for user-friendly build system) -* OpenSSL -* Pthreads (for POSIX systems it's builtin, for Windows there's a library) - -For detailed description of the build system and options, please read [SRT Build Options](docs/build/build-options.md). +* C++03 (or above) compliant compiler. +* CMake 2.8.12 or above (as build system). +* OpenSSL 1.1 (to enable encryption, or build with `-DENABLE_ENCRYPTION=OFF`). +* Multithreading is provided by either of the following: + * C++11: standard library (`std` by `-DENABLE_STDCXX_SYNC=ON` CMake option); + * C++03: Pthreads (for POSIX systems it's built in, for Windows there is a ported library). +* Tcl 8.5 (optional, used by `./configure` script or use CMake directly). + +For a detailed description of the build system and options, please refer to [SRT Build Options](docs/build/build-options.md). ### Build on Linux From e761745eaf8c2d1a371d1a3043f0de8c449e4f95 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 7 Jun 2021 15:39:34 +0200 Subject: [PATCH 198/790] [tests] Added fixes for FEC test occasional failure (#2037) --- test/test_fec_rebuilding.cpp | 56 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 8ea7ea26b..17f7cfbc9 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -297,7 +297,7 @@ TEST(TestFEC, Connection) ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); - + srt_listen(l, 1); auto connect_res = std::async(std::launch::async, [&s, &sa]() { @@ -305,19 +305,21 @@ TEST(TestFEC, Connection) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + // Given 2s timeout for accepting as it has occasionally happened with Travis + // that 1s might not be enough. + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; - srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); - srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + EXPECT_NE(srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size), -1); + EXPECT_NE(srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size), -1); string caller_config = result_config1; string accept_config = result_config2; @@ -358,15 +360,15 @@ TEST(TestFEC, ConnectionReorder) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -411,15 +413,15 @@ TEST(TestFEC, ConnectionFull1) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -463,15 +465,15 @@ TEST(TestFEC, ConnectionFull2) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -516,15 +518,15 @@ TEST(TestFEC, ConnectionMess) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -567,15 +569,15 @@ TEST(TestFEC, ConnectionForced) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); From e63b3580cbca5e7b73e172b371cb822477b84d09 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 11 Jun 2021 19:30:43 +0200 Subject: [PATCH 199/790] [apps] Output json stats values as numbers --- apps/apputil.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0333c768f..bf808a95d 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -484,7 +484,6 @@ class SrtStatsJson : public SrtStatsWriter string WriteStats(int sid, const CBytePerfMon& mon) override { std::ostringstream output; - static const string qt = R"(")"; string pretty_cr, pretty_tab; if (Option("pretty")) @@ -540,9 +539,7 @@ class SrtStatsJson : public SrtStatsWriter // Print the current field output << quotekey(i->name); - output << qt; i->PrintValue(output, mon); - output << qt; } // Close the previous subcategory From 16eca6b404a697c705ab7d37af9ea852cd77936e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 16 Jun 2021 18:59:13 +0200 Subject: [PATCH 200/790] [docs] Wrong error code for srt_accept_bond when timed out (#2040) --- docs/API/API-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 5a1fffd70..d59ce481e 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -646,7 +646,7 @@ calling this function. | [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `listeners` or `nlisteners` < 1 | | [`SRT_EINVSOCK`](#srt_einvsock) | Any socket in `listeners` designates no valid socket ID. Can also mean *Internal Error* when
an error occurred while creating an accepted socket (:warning:   **BUG?**) | | [`SRT_ENOLISTEN`](#srt_enolisten) | Any socket in `listeners` is not set up as a listener ([`srt_listen`](#srt_listen) not called, or the listener socket
has already been closed) | -| [`SRT_EASYNCRCV`](#srt_easyncrcv) | No connection reported on any listener socket as the timeout has been reached. This error is only
reported when `msTimeOut` is not -1 | +| [`SRT_ETIMEOUT`](#srt_etimeout) | No connection reported on any listener socket as the timeout has been reached. This error is only
reported when `msTimeOut` is not -1 | | | | From 8c4f288a5ef2b737e83bd77940dc9794c0e82a53 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 17 Jun 2021 15:56:30 +0200 Subject: [PATCH 201/790] [apps] Refactored app support components to make them more reusable (#2042) * Extracted componentable parts from srt-test-relay * Fixed: VerbLock is in use by testactivemedia --- apps/apputil.hpp | 15 ++ apps/logsupport.cpp | 31 +++ apps/logsupport.hpp | 2 + apps/verbose.hpp | 18 ++ testing/srt-test-live.cpp | 31 --- testing/srt-test-relay.cpp | 326 ++--------------------------- testing/srt-test-relay.maf | 1 + testing/testactivemedia.cpp | 120 +++++++++++ testing/testactivemedia.hpp | 188 +++++++++++++++++ testing/testmedia.cpp | 395 ++++++++++++++++-------------------- testing/testmedia.hpp | 53 ++++- testing/testmediabase.hpp | 4 +- 12 files changed, 630 insertions(+), 554 deletions(-) create mode 100644 testing/testactivemedia.cpp create mode 100644 testing/testactivemedia.hpp diff --git a/apps/apputil.hpp b/apps/apputil.hpp index f7bf83df3..4f2b84bf7 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -90,6 +90,21 @@ const int SysAGAIN = EAGAIN; sockaddr_any CreateAddr(const std::string& name, unsigned short port = 0, int pref_family = AF_UNSPEC); std::string Join(const std::vector& in, std::string sep); +template +struct OnReturnSetter +{ + VarType& var; + ValType value; + + OnReturnSetter(VarType& target, ValType v): var(target), value(v) {} + ~OnReturnSetter() { var = value; } +}; + +template +OnReturnSetter OnReturnSet(VarType& target, ValType v) +{ return OnReturnSetter(target, v); } + +// ---- OPTIONS MODULE inline bool CheckTrue(const std::vector& in) { diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index 2acbf64cc..fbd70c47e 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -171,4 +171,35 @@ set SrtParseLogFA(string fa, set* punknown) return fas; } +void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) +{ + std::ostringstream son, soff; + + for (auto& s: speclist) + { + string name; + bool on = true; + if (s[0] == '+') + name = s.substr(1); + else if (s[0] == '~') + { + name = s.substr(1); + on = false; + } + else + name = s; + + if (on) + son << "," << name; + else + soff << "," << name; + } + + const string& sons = son.str(); + const string& soffs = soff.str(); + + w_on = sons.empty() ? string() : sons.substr(1); + w_off = soffs.empty() ? string() : soffs.substr(1); +} + diff --git a/apps/logsupport.hpp b/apps/logsupport.hpp index 63e732560..79115d726 100644 --- a/apps/logsupport.hpp +++ b/apps/logsupport.hpp @@ -13,11 +13,13 @@ #include #include +#include #include "../srtcore/srt.h" #include "../srtcore/logging_api.h" srt_logging::LogLevel::type SrtParseLogLevel(std::string level); std::set SrtParseLogFA(std::string fa, std::set* punknown = nullptr); +void ParseLogFASpec(const std::vector& speclist, std::string& w_on, std::string& w_off); const std::map SrtLogFAList(); SRT_API extern std::map srt_level_names; diff --git a/apps/verbose.hpp b/apps/verbose.hpp index ec0276c12..10591888b 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -74,11 +74,29 @@ class ErrLog: public Log } }; +// terminal +inline void Print(Log& ) {} + +template +inline void Print(Log& out, Arg1&& arg1, Args&&... args) +{ + out << arg1; + Print(out, args...); +} + } inline Verbose::Log Verb() { return Verbose::Log(); } inline Verbose::ErrLog Verror() { return Verbose::ErrLog(); } +template +inline void Verb(Args&&... args) +{ + Verbose::Log log; + Verbose::Print(log, args...); +} + + // Manipulator tags static const Verbose::LogNoEol VerbNoEOL; #if SRT_ENABLE_VERBOSE_LOCK diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 5977b52b7..5bac09d78 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -378,37 +378,6 @@ extern "C" int SrtRejectByCodeHook(void* op, SRTSOCKET acpsock, int , const sock return -1; } -void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) -{ - std::ostringstream son, soff; - - for (auto& s: speclist) - { - string name; - bool on = true; - if (s[0] == '+') - name = s.substr(1); - else if (s[0] == '~') - { - name = s.substr(1); - on = false; - } - else - name = s; - - if (on) - son << "," << name; - else - soff << "," << name; - } - - const string& sons = son.str(); - const string& soffs = soff.str(); - - w_on = sons.empty() ? string() : sons.substr(1); - w_off = soffs.empty() ? string() : soffs.substr(1); -} - 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 141bb8e83..1214125b7 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -15,6 +15,7 @@ written by #include "platform_sys.h" +#include #include #include #include @@ -32,6 +33,8 @@ written by #include #include +#include "testactivemedia.hpp" + #include "apputil.hpp" #include "uriparser.hpp" #include "logsupport.hpp" @@ -41,13 +44,15 @@ written by #include "testmedia.hpp" #include "threadname.h" + + + bool Upload(UriParser& srt, UriParser& file); bool Download(UriParser& srt, UriParser& file); srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-relay"); -volatile bool g_program_interrupted = false; -volatile bool g_program_established = false; +std::atomic g_program_established {false}; SrtModel* g_pending_model = nullptr; @@ -56,7 +61,7 @@ thread::id g_root_thread = std::this_thread::get_id(); static void OnINT_SetInterrupted(int) { Verb() << VerbLock << "SIGINT: Setting interrupt state."; - ::g_program_interrupted = true; + ::transmit_int_state = true; // Just for a case, forcefully close all active SRT sockets. SrtModel* pm = ::g_pending_model; @@ -83,168 +88,10 @@ static void OnINT_SetInterrupted(int) using namespace std; -template -struct OnReturnSetter -{ - VarType& var; - ValType value; - - OnReturnSetter(VarType& target, ValType v): var(target), value(v) {} - ~OnReturnSetter() { var = value; } -}; - -template -OnReturnSetter OnReturnSet(VarType& target, ValType v) -{ return OnReturnSetter(target, v); } - -template -struct Medium -{ - class SrtMainLoop* master = nullptr; - MediumDir* med = nullptr; - unique_ptr pinned_med; - list buffer; - mutex buffer_lock; - thread thr; - condition_variable ready; - volatile bool running = false; - std::exception_ptr xp; // To catch exception thrown by a thread - - virtual void Runner() = 0; - - void RunnerBase() - { - try - { - Runner(); - } - catch (...) - { - xp = std::current_exception(); - } - - //Verb() << "Medium: " << this << ": thread exit"; - unique_lock g(buffer_lock); - running = false; - ready.notify_all(); - //Verb() << VerbLock << "Medium: EXIT NOTIFIED"; - } - - void run() - { - running = true; - std::ostringstream tns; - tns << typeid(*this).name() << ":" << this; - ThreadName tn(tns.str().c_str()); - thr = thread( [this] { RunnerBase(); } ); - } - - void quit() - { - if (!med) - return; - - applog.Debug() << "Medium(" << typeid(*med).name() << ") quit. Buffer contains " << buffer.size() << " blocks"; - - string name; - if (Verbose::on) - name = typeid(*med).name(); - - med->Close(); - if (thr.joinable()) - { - applog.Debug() << "Medium::quit: Joining medium thread (" << name << ") ..."; - thr.join(); - applog.Debug() << "... done"; - } - - if (xp) - { - try { - std::rethrow_exception(xp); - } catch (TransmissionError& e) { - if (Verbose::on) - Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); - else - cerr << "Transmission Error: " << e.what() << endl; - } catch (...) { - if (Verbose::on) - Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; - else - cerr << "UNKNOWN EXCEPTION on medium\n"; - } - } - - // Prevent further quits from running - med = nullptr; - } - - void Setup(SrtMainLoop* mst, MediumDir* t) - { - med = t; - master = mst; - // Leave pinned_med as 0 - } - - void Setup(SrtMainLoop* mst, unique_ptr&& medbase) - { - pinned_med = move(medbase); - med = pinned_med.get(); - master = mst; - } - - virtual ~Medium() - { - //Verb() << "Medium: " << this << " DESTROYED. Threads quit."; - quit(); - } -}; - size_t g_chunksize = 0; size_t g_default_live_chunksize = 1316; size_t g_default_file_chunksize = 1456; -struct SourceMedium: Medium -{ - // Source Runner: read payloads and put on the buffer - void Runner() override; - - // External user: call this to get the buffer. - MediaPacket Extract(); -}; - -struct TargetMedium: Medium -{ - void Runner() override; - - bool Schedule(const MediaPacket& data) - { - lock_guard lg(buffer_lock); - if (!running || ::g_program_interrupted) - return false; - - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << data.payload.size() << "] CLIENT -> BUFFER"; - buffer.push_back(data); - ready.notify_one(); - return true; - } - - void Interrupt() - { - lock_guard lg(buffer_lock); - running = false; - ready.notify_one(); - } - - ~TargetMedium() - { - //Verb() << "TargetMedium: DESTROYING"; - Interrupt(); - // ~Medium will do quit() additionally, which joins the thread - } - -}; - class SrtMainLoop { UriParser m_srtspec; @@ -275,140 +122,6 @@ class SrtMainLoop } }; - -void SourceMedium::Runner() -{ - ThreadName::set("SourceRN"); - if (!master) - { - cerr << "IPE: incorrect setup, master empty\n"; - return; - } - - /* Don't stop me now... - struct OnReturn - { - SrtMainLoop* m; - OnReturn(SrtMainLoop* mst): m(mst) {} - ~OnReturn() - { - m->MakeStop(); - } - } on_return(master); - */ - - Verb() << VerbLock << "Starting SourceMedium: " << this; - for (;;) - { - auto input = med->Read(g_chunksize); - if (input.payload.empty() && med->End()) - { - Verb() << VerbLock << "Exiting SourceMedium: " << this; - return; - } - applog.Debug() << "SourceMedium(" << typeid(*med).name() << "): [" << input.payload.size() << "] MEDIUM -> BUFFER. signal(" << &ready << ")"; - - lock_guard g(buffer_lock); - buffer.push_back(input); - ready.notify_one(); - } -} - -MediaPacket SourceMedium::Extract() -{ - if (!master) - return {}; - - unique_lock g(buffer_lock); - for (;;) - { - if (!buffer.empty()) - { - MediaPacket top; - swap(top, *buffer.begin()); - buffer.pop_front(); - applog.Debug() << "SourceMedium(" << typeid(*med).name() << "): [" << top.payload.size() << "] BUFFER -> CLIENT"; - return top; - } - else - { - // Don't worry about the media status as long as you have somthing in the buffer. - // Purge the buffer first, then worry about the other things. - if (!running || ::g_program_interrupted) - { - applog.Debug() << "Extract(" << typeid(*med).name() << "): INTERRUPTED READING (" - << (!running ? "local" : (!master->IsRunning() ? "master" : "unknown")) << ")"; - //Verb() << "SourceMedium " << this << " not running"; - return {}; - } - - } - - // Block until ready - applog.Debug() << "Extract(" << typeid(*med).name() << "): " << this << " wait(" << &ready << ") -->"; - ready.wait_for(g, chrono::seconds(1), [this] { return running && master->IsRunning() && !buffer.empty(); }); - applog.Debug() << "Extract(" << typeid(*med).name() << "): " << this << " <-- notified (running:" - << boolalpha << running << " master:" << master->IsRunning() << " buffer:" << buffer.size() << ")"; - } -} - -void TargetMedium::Runner() -{ - ThreadName::set("TargetRN"); - auto on_return_set = OnReturnSet(running, false); - Verb() << VerbLock << "Starting TargetMedium: " << this; - for (;;) - { - MediaPacket val; - { - unique_lock lg(buffer_lock); - if (buffer.empty()) - { - if (!running) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): buffer empty, medium stopped, exiting."; - return; - } - - bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER update (timeout:" - << boolalpha << gotsomething << " running: " << running << ")"; - if (::g_program_interrupted || !running || !med || med->Broken()) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): buffer empty, medium " - << (!::g_program_interrupted ? - (running ? - (med ? - (med->Broken() ? "broken" : "UNKNOWN") - : "deleted") - : "stopped") - : "killed"); - return; - } - if (!gotsomething) // exit on timeout - continue; - } - swap(val, *buffer.begin()); - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER extraction"; - - buffer.pop_front(); - } - - // Check before writing - if (med->Broken()) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER -> DISCARDED (medium broken)"; - running = false; - return; - } - - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER -> MEDIUM"; - // You get the data to send, send them. - med->Write(val); - } -} - - int main( int argc, char** argv ) { OptionName @@ -605,7 +318,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin { Verb() << "Setting up output: " << spec; unique_ptr m { new TargetMedium }; - m->Setup(this, Target::Create(spec)); + m->Setup(Target::Create(spec)); m_output_media.push_back(move(m)); } @@ -631,14 +344,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "... Established. configuring other pipes:"; // Once it's ready, use it to initialize the medium. - - m_srt_relay.reset(new SrtRelay); - m_srt_relay->StealFrom(m); - - m_srt_source.Setup(this, m_srt_relay.get()); - bool file_mode = (transtype == "file"); - if (g_chunksize == 0) { if (file_mode) @@ -649,6 +355,11 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "DEFAULT CHUNKSIZE used: " << g_chunksize; } + m_srt_relay.reset(new SrtRelay); + m_srt_relay->StealFrom(m); + + m_srt_source.Setup(m_srt_relay.get(), g_chunksize); + // Now check the input medium if (input_echoback) { @@ -656,7 +367,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin // Add SRT medium to output targets, and keep input medium empty. unique_ptr m { new TargetMedium }; - m->Setup(this, m_srt_relay.get()); + m->Setup(m_srt_relay.get()); m_output_media.push_back(move(m)); } else @@ -665,7 +376,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin // to the output list, as this will be fed directly // by the data from this input medium in a spearate engine. Verb() << "Setting up input: " << input_spec; - m_input_medium.Setup(this, Source::Create(input_spec)); + m_input_medium.Setup(Source::Create(input_spec), g_chunksize); if (!file_mode) { @@ -796,6 +507,11 @@ void SrtMainLoop::run() } Verb() << "MEDIA LOOP EXIT"; + for (auto& m : m_output_media) + { + m->quit(); + } + m_input_medium.quit(); m_srt_source.quit(); if (m_input_xp) diff --git a/testing/srt-test-relay.maf b/testing/srt-test-relay.maf index b1b22afb2..33cba8173 100644 --- a/testing/srt-test-relay.maf +++ b/testing/srt-test-relay.maf @@ -3,6 +3,7 @@ SOURCES srt-test-relay.cpp testmedia.cpp +testactivemedia.cpp ../apps/apputil.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp new file mode 100644 index 000000000..765d22ef6 --- /dev/null +++ b/testing/testactivemedia.cpp @@ -0,0 +1,120 @@ + +#include "testactivemedia.hpp" + +void SourceMedium::Runner() +{ + ThreadName::set("SourceRN"); + + Verb() << VerbLock << "Starting SourceMedium: " << this; + for (;;) + { + auto input = med->Read(chunksize_); + if (input.payload.empty() && med->End()) + { + Verb() << VerbLock << "Exiting SourceMedium: " << this; + return; + } + LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", input.payload.size(), "] MEDIUM -> BUFFER. signal(", &ready, ")"); + + lock_guard g(buffer_lock); + buffer.push_back(input); + ready.notify_one(); + } +} + +MediaPacket SourceMedium::Extract() +{ + unique_lock g(buffer_lock); + for (;;) + { + if (::transmit_int_state) + running = false; + + if (!buffer.empty()) + { + MediaPacket top; + swap(top, *buffer.begin()); + buffer.pop_front(); + LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", top.payload.size(), "] BUFFER -> CLIENT"); + return top; + } + else + { + // Don't worry about the media status as long as you have somthing in the buffer. + // Purge the buffer first, then worry about the other things. + if (!running) + { + //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): INTERRUPTED READING"); + //Verb() << "SourceMedium " << this << " not running"; + return {}; + } + + } + + // Block until ready + //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " wait(", &ready, ") -->"); + + ready.wait_for(g, chrono::seconds(1), [this] { return running && !buffer.empty(); }); + + // LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " <-- notified (running:" + // << boolalpha << running << " buffer:" << buffer.size() << ")"); + } +} + +void TargetMedium::Runner() +{ + ThreadName::set("TargetRN"); + auto on_return_set = OnReturnSet(running, false); + Verb() << VerbLock << "Starting TargetMedium: " << this; + for (;;) + { + MediaPacket val; + { + unique_lock lg(buffer_lock); + if (buffer.empty()) + { + if (!running) + { + //LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium stopped, exiting."); + return; + } + + bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:", + boolalpha, gotsomething, " running: ", running, ")"); + if (::transmit_int_state || !running || !med || med->Broken()) + { + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ", + (!::transmit_int_state ? + (running ? + (med ? + (med->Broken() ? "broken" : "UNKNOWN") + : "deleted") + : "stopped") + : "killed")); + return; + } + if (!gotsomething) // exit on timeout + continue; + } + swap(val, *buffer.begin()); + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER extraction"); + + buffer.pop_front(); + } + + // Check before writing + if (med->Broken()) + { + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> DISCARDED (medium broken)"); + running = false; + return; + } + + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> MEDIUM"); + // You get the data to send, send them. + med->Write(val); + } +} + + diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp new file mode 100644 index 000000000..b94ff9aa3 --- /dev/null +++ b/testing/testactivemedia.hpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "testmedia.hpp" +#include "logsupport.hpp" + +#define SRT_ENABLE_VERBOSE_LOCK 1 +#include "verbose.hpp" + +#include "logging.h" +#include "threadname.h" + +extern srt_logging::Logger applog; + +template +struct Medium +{ + MediumDir* med = nullptr; + std::unique_ptr pinned_med; + std::list buffer; + std::mutex buffer_lock; + std::thread thr; + std::condition_variable ready; + std::atomic running {false}; + std::exception_ptr xp; // To catch exception thrown by a thread + + virtual void Runner() = 0; + + void RunnerBase() + { + try + { + running = true; + Runner(); + } + catch (...) + { + xp = std::current_exception(); + } + + //Verb() << "Medium: " << this << ": thread exit"; + std::unique_lock g(buffer_lock); + running = false; + ready.notify_all(); + //Verb() << VerbLock << "Medium: EXIT NOTIFIED"; + } + + void run() + { + running = true; + std::ostringstream tns; + tns << typeid(*this).name() << ":" << this; + ThreadName tn(tns.str().c_str()); + thr = thread( [this] { RunnerBase(); } ); + } + + void quit() + { + if (!med) + return; + + LOGP(applog.Debug, "Medium(", typeid(*med).name(), ") quit. Buffer contains ", buffer.size(), " blocks"); + + std::string name; + if (Verbose::on) + name = typeid(*med).name(); + + med->Close(); + if (thr.joinable()) + { + LOGP(applog.Debug, "Medium::quit: Joining medium thread (", name, ") ..."); + thr.join(); + LOGP(applog.Debug, "... done"); + } + + if (xp) + { + try { + std::rethrow_exception(xp); + } catch (TransmissionError& e) { + if (Verbose::on) + Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); + else + cerr << "Transmission Error: " << e.what() << endl; + } catch (...) { + if (Verbose::on) + Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; + else + cerr << "UNKNOWN EXCEPTION on medium\n"; + } + } + + // Prevent further quits from running + med = nullptr; + } + + void Setup(MediumDir* t) + { + med = t; + // Leave pinned_med as 0 + } + + void Setup(std::unique_ptr&& medbase) + { + pinned_med = std::move(medbase); + med = pinned_med.get(); + } + + virtual ~Medium() + { + //Verb() << "Medium: " << this << " DESTROYED. Threads quit."; + quit(); + } + + virtual void Start() { run(); } + virtual void Stop() { quit(); } +}; + +struct SourceMedium: Medium +{ + size_t chunksize_ = 0; + typedef Medium Base; + + // Source Runner: read payloads and put on the buffer + void Runner() override; + + // External user: call this to get the buffer. + MediaPacket Extract(); + + template + void Setup(Arg&& medium, size_t chunksize) + { + chunksize_ = chunksize; + return Base::Setup(std::move(medium)); + } +}; + +struct TargetMedium: Medium +{ + void Runner() override; + + bool Schedule(const MediaPacket& data) + { + LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); + lock_guard lg(buffer_lock); + LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); + if (!running || ::transmit_int_state) + { + LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); + return false; + } + + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): Schedule: [", data.payload.size(), "] CLIENT -> BUFFER"); + buffer.push_back(data); + ready.notify_one(); + return true; + } + + void Clear() + { + lock_guard lg(buffer_lock); + buffer.clear(); + } + + void Interrupt() + { + lock_guard lg(buffer_lock); + running = false; + ready.notify_one(); + } + + ~TargetMedium() + { + //Verb() << "TargetMedium: DESTROYING"; + Interrupt(); + // ~Medium will do quit() additionally, which joins the thread + } +}; + + diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 501e5f8d7..f986aa5dc 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #if !defined(_WIN32) #include @@ -48,8 +49,8 @@ using srt_logging::SockStatusStr; using srt_logging::MemberStatusStr; #endif -volatile bool transmit_throw_on_interrupt = false; -volatile bool transmit_int_state = false; +std::atomic transmit_throw_on_interrupt {false}; +std::atomic transmit_int_state {false}; int transmit_bw_report = 0; unsigned transmit_stats_report = 0; size_t transmit_chunk_size = SRT_LIVE_DEF_PLSIZE; @@ -2715,276 +2716,238 @@ static inline bool IsMulticast(in_addr adr) return c >= 224 && c <= 239; } - -class UdpCommon +void UdpCommon::Setup(string host, int port, map attr) { -protected: - int m_sock = -1; - sockaddr_any sadr; - string adapter; - map m_options; + m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (m_sock == -1) + Error(SysError(), "UdpCommon::Setup: socket"); - void Setup(string host, int port, map attr) - { - m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (m_sock == -1) - Error(SysError(), "UdpCommon::Setup: socket"); + int yes = 1; + ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes); - int yes = 1; - ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes); + sadr = CreateAddr(host, port); - sadr = CreateAddr(host, port); + bool is_multicast = false; + if (sadr.family() == AF_INET) + { + if (attr.count("multicast")) + { + if (!IsMulticast(sadr.sin.sin_addr)) + { + throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address"); + } + is_multicast = true; + } + else if (IsMulticast(sadr.sin.sin_addr)) + { + is_multicast = true; + } - bool is_multicast = false; - if (sadr.family() == AF_INET) + if (is_multicast) { - if (attr.count("multicast")) + ip_mreq_source mreq_ssm; + ip_mreq mreq; + sockaddr_any maddr; + int opt_name; + void* mreq_arg_ptr; + socklen_t mreq_arg_size; + + adapter = attr.count("adapter") ? attr.at("adapter") : string(); + if (adapter == "") { - if (!IsMulticast(sadr.sin.sin_addr)) - { - throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address"); - } - is_multicast = true; + Verb() << "Multicast: home address: INADDR_ANY:" << port; + maddr.sin.sin_family = AF_INET; + maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY); + maddr.sin.sin_port = htons(port); // necessary for temporary use } - else if (IsMulticast(sadr.sin.sin_addr)) + else { - is_multicast = true; + Verb() << "Multicast: home address: " << adapter << ":" << port; + maddr = CreateAddr(adapter, port); } - if (is_multicast) + if (attr.count("source")) { - ip_mreq_source mreq_ssm; - ip_mreq mreq; - sockaddr_any maddr; - int opt_name; - void* mreq_arg_ptr; - socklen_t mreq_arg_size; - - adapter = attr.count("adapter") ? attr.at("adapter") : string(); - if (adapter == "") - { - Verb() << "Multicast: home address: INADDR_ANY:" << port; - maddr.sin.sin_family = AF_INET; - maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY); - maddr.sin.sin_port = htons(port); // necessary for temporary use - } - else - { - Verb() << "Multicast: home address: " << adapter << ":" << port; - maddr = CreateAddr(adapter, port); - } - - if (attr.count("source")) - { - /* this is an ssm. we need to use the right struct and opt */ - opt_name = IP_ADD_SOURCE_MEMBERSHIP; - mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; - mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; - inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr); - mreq_arg_size = sizeof(mreq_ssm); - mreq_arg_ptr = &mreq_ssm; - } - else - { - opt_name = IP_ADD_MEMBERSHIP; - mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; - mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; - mreq_arg_size = sizeof(mreq); - mreq_arg_ptr = &mreq; - } + /* this is an ssm. we need to use the right struct and opt */ + opt_name = IP_ADD_SOURCE_MEMBERSHIP; + mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; + mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; + inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr); + mreq_arg_size = sizeof(mreq_ssm); + mreq_arg_ptr = &mreq_ssm; + } + else + { + opt_name = IP_ADD_MEMBERSHIP; + mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; + mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; + mreq_arg_size = sizeof(mreq); + mreq_arg_ptr = &mreq; + } #ifdef _WIN32 - const char* mreq_arg = (const char*)mreq_arg_ptr; - const auto status_error = SOCKET_ERROR; + const char* mreq_arg = (const char*)mreq_arg_ptr; + const auto status_error = SOCKET_ERROR; #else - const void* mreq_arg = mreq_arg_ptr; - const auto status_error = -1; + const void* mreq_arg = mreq_arg_ptr; + const auto status_error = -1; #endif #if defined(_WIN32) || defined(__CYGWIN__) - // On Windows it somehow doesn't work when bind() - // is called with multicast address. Write the address - // that designates the network device here. - // Also, sets port sharing when working with multicast - sadr = maddr; - int reuse = 1; - int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)); - if (shareAddrRes == status_error) - { - throw runtime_error("marking socket for shared use failed"); - } - Verb() << "Multicast(Windows): will bind to home address"; + // On Windows it somehow doesn't work when bind() + // is called with multicast address. Write the address + // that designates the network device here. + // Also, sets port sharing when working with multicast + sadr = maddr; + int reuse = 1; + int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)); + if (shareAddrRes == status_error) + { + throw runtime_error("marking socket for shared use failed"); + } + Verb() << "Multicast(Windows): will bind to home address"; #else - Verb() << "Multicast(POSIX): will bind to IGMP address: " << host; + Verb() << "Multicast(POSIX): will bind to IGMP address: " << host; #endif - int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size); - - if (res == status_error) - { - Error(errno, "adding to multicast membership failed"); - } + int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size); - attr.erase("multicast"); - attr.erase("adapter"); + if (res == status_error) + { + Error(errno, "adding to multicast membership failed"); } + + attr.erase("multicast"); + attr.erase("adapter"); } + } - // The "ttl" options is handled separately, it maps to both IP_TTL - // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast. - if (attr.count("ttl")) - { - int ttl = stoi(attr.at("ttl")); - int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl); - if (res == -1) - Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl; - res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl); - if (res == -1) - Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl; + // The "ttl" options is handled separately, it maps to both IP_TTL + // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast. + if (attr.count("ttl")) + { + int ttl = stoi(attr.at("ttl")); + int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl); + if (res == -1) + Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl; + res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl); + if (res == -1) + Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl; - attr.erase("ttl"); - } + attr.erase("ttl"); + } - m_options = attr; + m_options = attr; - for (auto o: udp_options) + for (auto o: udp_options) + { + // Ignore "binding" - for UDP there are no post options. + if (m_options.count(o.name)) { - // Ignore "binding" - for UDP there are no post options. - if (m_options.count(o.name)) - { - string value = m_options.at(o.name); - bool ok = o.apply(m_sock, value); - if (!ok) - Verb() << "WARNING: failed to set '" << o.name << "' to " << value; - } + string value = m_options.at(o.name); + bool ok = o.apply(m_sock, value); + if (!ok) + Verb() << "WARNING: failed to set '" << o.name << "' to " << value; } } +} - void Error(int err, string src) - { - char buf[512]; - string message = SysStrError(err, buf, 512u); +void UdpCommon::Error(int err, string src) +{ + char buf[512]; + string message = SysStrError(err, buf, 512u); - if (Verbose::on) - Verb() << "FAILURE\n" << src << ": [" << err << "] " << message; - else - cerr << "\nERROR #" << err << ": " << message << endl; + if (Verbose::on) + Verb() << "FAILURE\n" << src << ": [" << err << "] " << message; + else + cerr << "\nERROR #" << err << ": " << message << endl; - throw TransmissionError("error: " + src + ": " + message); - } + throw TransmissionError("error: " + src + ": " + message); +} - ~UdpCommon() - { +UdpCommon::~UdpCommon() +{ #ifdef _WIN32 - if (m_sock != -1) - { - shutdown(m_sock, SD_BOTH); - closesocket(m_sock); - m_sock = -1; - } + if (m_sock != -1) + { + shutdown(m_sock, SD_BOTH); + closesocket(m_sock); + m_sock = -1; + } #else - close(m_sock); + close(m_sock); #endif - } -}; - +} -class UdpSource: public virtual Source, public virtual UdpCommon +UdpSource::UdpSource(string host, int port, const map& attr) { - bool eof = true; -public: + Setup(host, port, attr); + int stat = ::bind(m_sock, sadr.get(), sadr.size()); + if (stat == -1) + Error(SysError(), "Binding address for UDP"); + eof = false; + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) + Error(SysError(), "Setting timeout for UDP"); +} - UdpSource(string host, int port, const map& attr) +MediaPacket UdpSource::Read(size_t chunk) +{ + bytevector data(chunk); + sockaddr_any sa(sadr.family()); + int64_t srctime = 0; +AGAIN: + int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen()); + int err = SysError(); + if (transmit_use_sourcetime) { - Setup(host, port, attr); - int stat = ::bind(m_sock, sadr.get(), sadr.size()); - if (stat == -1) - Error(SysError(), "Binding address for UDP"); - eof = false; - struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) - Error(SysError(), "Setting timeout for UDP"); + srctime = srt_time_now(); } - - MediaPacket Read(size_t chunk) override + if (stat == -1) { - bytevector data(chunk); - sockaddr_any sa(sadr.family()); - int64_t srctime = 0; -AGAIN: - int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen()); - int err = SysError(); - if (transmit_use_sourcetime) - { - srctime = srt_time_now(); - } - if (stat == -1) - { - if (!::transmit_int_state && err == SysAGAIN) - goto AGAIN; + if (!::transmit_int_state && err == SysAGAIN) + goto AGAIN; - Error(SysError(), "UDP Read/recvfrom"); - } - - if (stat < 1) - { - eof = true; - return bytevector(); - } - - chunk = size_t(stat); - if (chunk < data.size()) - data.resize(chunk); + Error(SysError(), "UDP Read/recvfrom"); + } - return MediaPacket(data, srctime); + if (stat < 1) + { + eof = true; + return bytevector(); } - bool IsOpen() override { return m_sock != -1; } - bool End() override { return eof; } -}; + chunk = size_t(stat); + if (chunk < data.size()) + data.resize(chunk); + + return MediaPacket(data, srctime); +} -class UdpTarget: public virtual Target, public virtual UdpCommon +UdpTarget::UdpTarget(string host, int port, const map& attr) { -public: - UdpTarget(string host, int port, const map& attr ) + Setup(host, port, attr); + if (adapter != "") { - Setup(host, port, attr); - if (adapter != "") - { - auto maddr = CreateAddr(adapter, 0); - in_addr addr = maddr.sin.sin_addr; + auto maddr = CreateAddr(adapter, 0); + in_addr addr = maddr.sin.sin_addr; - int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast(&addr), sizeof(addr)); - if (res == -1) - { - Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter); - } + int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast(&addr), sizeof(addr)); + if (res == -1) + { + Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter); } } +} - void Write(const MediaPacket& data) override - { - int stat = sendto(m_sock, data.payload.data(), data.payload.size(), 0, (sockaddr*)&sadr, sizeof sadr); - if (stat == -1) - Error(SysError(), "UDP Write/sendto"); - } - - bool IsOpen() override { return m_sock != -1; } - bool Broken() override { return false; } -}; - -class UdpRelay: public Relay, public UdpSource, public UdpTarget +void UdpTarget::Write(const MediaPacket& data) { -public: - UdpRelay(string host, int port, const map& attr): - UdpSource(host, port, attr), - UdpTarget(host, port, attr) - { - } + int stat = sendto(m_sock, data.payload.data(), data.payload.size(), 0, (sockaddr*)&sadr, sizeof sadr); + if (stat == -1) + Error(SysError(), "UDP Write/sendto"); +} - bool IsOpen() override { return m_sock != -1; } -}; template struct Udp; template <> struct Udp { typedef UdpSource type; }; diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index b251f140b..d3447a0d0 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -15,14 +15,16 @@ #include #include #include +#include +#include "apputil.hpp" #include "testmediabase.hpp" #include // Needs access to CUDTException #include extern srt_listen_callback_fn* transmit_accept_hook_fn; extern void* transmit_accept_hook_op; -extern volatile bool transmit_int_state; +extern std::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; @@ -298,6 +300,55 @@ class SrtModel: public SrtCommon } }; +class UdpCommon +{ +protected: + int m_sock = -1; + sockaddr_any sadr; + std::string adapter; + std::map m_options; + void Setup(std::string host, int port, std::map attr); + void Error(int err, std::string src); + + ~UdpCommon(); +}; + + +class UdpSource: public virtual Source, public virtual UdpCommon +{ + bool eof = true; +public: + + UdpSource(string host, int port, const map& attr); + + MediaPacket Read(size_t chunk) override; + + bool IsOpen() override { return m_sock != -1; } + bool End() override { return eof; } +}; + +class UdpTarget: public virtual Target, public virtual UdpCommon +{ +public: + UdpTarget(string host, int port, const map& attr); + + void Write(const MediaPacket& data) override; + bool IsOpen() override { return m_sock != -1; } + bool Broken() override { return false; } +}; + +class UdpRelay: public Relay, public UdpSource, public UdpTarget +{ +public: + UdpRelay(string host, int port, const map& attr): + UdpSource(host, port, attr), + UdpTarget(host, port, attr) + { + } + + bool IsOpen() override { return m_sock != -1; } +}; + #endif diff --git a/testing/testmediabase.hpp b/testing/testmediabase.hpp index a04803e04..3eb16a4bb 100644 --- a/testing/testmediabase.hpp +++ b/testing/testmediabase.hpp @@ -17,8 +17,10 @@ #include #include +#include "uriparser.hpp" + typedef std::vector bytevector; -extern volatile bool transmit_throw_on_interrupt; +extern std::atomic transmit_throw_on_interrupt; extern int transmit_bw_report; extern unsigned transmit_stats_report; extern size_t transmit_chunk_size; From e932e8fbdda4a053023985a6f9c076cf09810e98 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 17:48:48 +0200 Subject: [PATCH 202/790] [core] Fixed getTsbPdTimeBase: carryover within 2 wrapping periods (#2043) --- srtcore/tsbpd_time.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index ff01f8a30..54a8c5df5 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -38,6 +38,7 @@ class drift_logger int64_t drift_sample, int64_t drift, int64_t overdrift, + const srt::sync::steady_clock::time_point& pkt_base, const srt::sync::steady_clock::time_point& tsbpd_base) { using namespace srt::sync; @@ -50,6 +51,9 @@ class drift_logger std::string str_tbase = srt::sync::FormatTime(tsbpd_base); str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part + std::string str_pkt_base = srt::sync::FormatTime(pkt_base); + str_pkt_base.resize(str_pkt_base.size() - 7); // remove trailing ' [STDY]' part + // m_fout << str_tnow << ","; m_fout << count_microseconds(steady_clock::now() - m_start_time) << ","; m_fout << ackack_timestamp << ","; @@ -57,6 +61,7 @@ class drift_logger m_fout << drift_sample << ","; m_fout << drift << ","; m_fout << overdrift << ","; + m_fout << str_pkt_base << ","; m_fout << str_tbase << "\n"; m_fout.flush(); } @@ -65,7 +70,7 @@ class drift_logger void print_header() { m_fout << "usElapsedStd,usAckAckTimestampStd,"; - m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,TSBPDBase\n"; + m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,tsPktBase,TSBPDBase\n"; } void create_file() @@ -121,8 +126,9 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, // A change in network delay has to be taken into account. The only way to get some estimation of it // is to estimate RTT change and assume that the change of the one way network delay is // approximated by the half of the RTT change. - const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); - const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp) - tdRTTDelta; + const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); + const time_point tsPktBaseTime = getPktTsbPdBaseTime(usPktTimestamp); + const steady_clock::duration tdDrift = tsNow - tsPktBaseTime - tdRTTDelta; const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); @@ -152,6 +158,7 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, count_microseconds(tdDrift), m_DriftTracer.drift(), m_DriftTracer.overdrift(), + tsPktBaseTime, m_tsTsbPdTimeBase); #endif return updated; @@ -168,7 +175,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra // // This function is called in the HSREQ reception handler only. m_tsTsbPdTimeBase = timebase; - m_tdTsbPdDelay = delay; + m_tdTsbPdDelay = delay; } void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, @@ -210,8 +217,11 @@ void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase, CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const { - const uint64_t carryover_us = - (m_bTsbPdWrapCheck && timestamp_us < TSBPD_WRAP_PERIOD) ? uint64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; + // A data packet within [TSBPD_WRAP_PERIOD; 2 * TSBPD_WRAP_PERIOD] would end TSBPD wrap-aware state. + // Some incoming control packets may not update the TSBPD base (calling updateTsbPdTimeBase(..)), + // but may come before a data packet with a timestamp in this range. Therefore the whole range should be tracked. + const int64_t carryover_us = + (m_bTsbPdWrapCheck && timestamp_us <= 2 * TSBPD_WRAP_PERIOD) ? int64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; return (m_tsTsbPdTimeBase + microseconds_from(carryover_us)); } @@ -243,14 +253,14 @@ void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp) return; } - // Check if timestamp is in the last 30 seconds before reaching the MAX_TIMESTAMP. + // Check if timestamp is within the TSBPD_WRAP_PERIOD before reaching the MAX_TIMESTAMP. if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) { // Approching wrap around point, start wrap check period (if for packet delivery head) m_bTsbPdWrapCheck = true; LOGC(tslog.Debug, - log << "tsbpd wrap period begins with ts=" << usPktTimestamp << " drift: " << m_DriftTracer.drift() - << "us."); + log << "tsbpd wrap period begins with ts=" << usPktTimestamp + << " TIME BASE: " << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us."); } } From 94322d4081b45153b6c1fe7785d6973b472de497 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 2 Jul 2021 21:06:28 +0800 Subject: [PATCH 203/790] [core] Fix unused-variable warning --- srtcore/api.cpp | 2 +- srtcore/buffer.cpp | 4 ++-- srtcore/channel.cpp | 2 +- srtcore/core.cpp | 12 ++++++------ srtcore/crypto.cpp | 7 ++++--- srtcore/epoll.cpp | 4 ++-- srtcore/fec.cpp | 2 +- srtcore/group.cpp | 6 +++--- srtcore/logging.h | 4 ++-- srtcore/sync.h | 3 ++- srtcore/utilities.h | 7 +++---- test/test_listen_callback.cpp | 2 +- testing/testmedia.cpp | 2 +- 13 files changed, 29 insertions(+), 28 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index aee13f389..d38442590 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1384,7 +1384,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // Set all options that were requested by the options set on a group // prior to connecting. - string error_reason ATR_UNUSED; + string error_reason SRT_ATR_UNUSED; try { for (size_t i = 0; i < g.m_config.size(); ++i) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e2eb4f406..bbe41b7f6 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -986,8 +986,8 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) int lastack = m_iLastAckPos; int rs = len; - int32_t trace_seq ATR_UNUSED = SRT_SEQNO_NONE; - int trace_shift ATR_UNUSED = -1; + int32_t trace_seq SRT_ATR_UNUSED = SRT_SEQNO_NONE; + int trace_shift SRT_ATR_UNUSED = -1; while ((p != lastack) && (rs > 0)) { diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 6e56e97f7..92fd6b991 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -183,7 +183,7 @@ void srt::CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - const int res ATR_UNUSED = + const int res SRT_ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); #if ENABLE_LOGGING if (res == -1) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7a337cf41..15a138a3a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2385,7 +2385,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // This function is called only when the URQ_CONCLUSION handshake has been received from the peer. bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, - uint32_t* out_data, + uint32_t* out_data SRT_ATR_UNUSED, size_t* pw_len) { // Initialize pw_len to 0 to handle the unencrypted case @@ -2440,7 +2440,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, uint32_t* p = reinterpret_cast(hspkt.m_pcData + CHandShake::m_iContentSize); size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0 - int hsreq_type_cmd ATR_UNUSED = SRT_CMD_NONE; + int hsreq_type_cmd SRT_ATR_UNUSED = SRT_CMD_NONE; if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ)) { @@ -2671,7 +2671,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_config.sCongestion.set("live", 4); } - bool have_group ATR_UNUSED = false; + bool have_group SRT_ATR_UNUSED = false; if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG)) { @@ -7715,7 +7715,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t first_seq ATR_UNUSED = ackDataUpTo(ack); + const int32_t first_seq SRT_ATR_UNUSED = ackDataUpTo(ack); InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8318,7 +8318,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), + const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), rtt, (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) @@ -10795,7 +10795,7 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) return debug_decision; } -bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) +bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason SRT_ATR_UNUSED) { // VERY HEAVY LOGGING #if ENABLE_HEAVY_LOGGING & 1 diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index c7c27abab..6c482a586 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -43,7 +43,7 @@ using namespace srt_logging; */ // 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE -const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000; +const int SRT_CRYPT_KM_PRE_ANNOUNCE SRT_ATR_UNUSED = 0x10000; namespace srt_logging { @@ -662,6 +662,8 @@ std::string CCryptoControl::CONID() const return os.str(); } +#ifdef SRT_ENABLE_ENCRYPTION + #if ENABLE_HEAVY_LOGGING static std::string CryptoFlags(int flg) { @@ -679,9 +681,8 @@ static std::string CryptoFlags(int flg) copy(f.begin(), f.end(), ostream_iterator(os, "|")); return os.str(); } -#endif +#endif // ENABLE_HEAVY_LOGGING -#ifdef SRT_ENABLE_ENCRYPTION bool CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto) { diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 71945bdbd..548418f88 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -713,7 +713,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); } - const bool wait_signaled ATR_UNUSED = CGlobEvent::waitForEvent(); + const bool wait_signaled SRT_ATR_UNUSED = CGlobEvent::waitForEvent(); HLOGC(ealog.Debug, log << "CEPoll::wait: EVENT WAITING: " << (wait_signaled ? "TRIGGERED" : "CHECKPOINT")); } @@ -779,7 +779,7 @@ int CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut, boo st[i->fd] = i->events; IF_HEAVY_LOGGING(singles << "@" << i->fd << ":"); IF_HEAVY_LOGGING(PrintEpollEvent(singles, i->events, i->parent->edgeOnly())); - const bool edged ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i` + const bool edged SRT_ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i` IF_HEAVY_LOGGING(singles << (edged ? "<^> " : " ")); } diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 31239eed8..61adc0957 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -2029,7 +2029,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t any_dismiss = true; const int32_t newbase = rcv.colq[numberCols()].base; - int32_t newbase_row ATR_UNUSED; // For logging only, but including FATAL. + int32_t newbase_row SRT_ATR_UNUSED; // For logging only, but including FATAL. // Sanity check // If sanity check failed OR if the number of existing row // groups doesn't enclose those that need to be dismissed, diff --git a/srtcore/group.cpp b/srtcore/group.cpp index a0be7dd72..f2f99201b 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3168,7 +3168,7 @@ CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_ } // [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime ATR_UNUSED, +bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime SRT_ATR_UNUSED, const int send_status, const int32_t lastseq, const int32_t pktseq, @@ -3769,7 +3769,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx // suffer linear search. int nwaiting = 0; - int nactivated ATR_UNUSED = 0; + int nactivated SRT_ATR_UNUSED = 0; int stat = -1; for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) { @@ -4217,7 +4217,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // sequences already used by the link from which packets were // copied to the backup buffer. IF_HEAVY_LOGGING(int32_t old = core.schedSeqNo()); - const bool su ATR_UNUSED = core.overrideSndSeqNo(curseq); + const bool su SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); HLOGC(gslog.Debug, log << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq << (su ? " - succeeded" : " - FAILED!")); diff --git a/srtcore/logging.h b/srtcore/logging.h index cfb239f1e..8767f2aa7 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -422,7 +422,7 @@ inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) } template -inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED) +inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING std::ostringstream serr; @@ -440,7 +440,7 @@ inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line AT #else template -inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED) +inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING std::ostringstream serr; diff --git a/srtcore/sync.h b/srtcore/sync.h index 53123c682..7da6952f7 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -51,6 +51,7 @@ #endif // ENABLE_STDCXX_SYNC +#include "srt.h" #include "utilities.h" class CUDTException; // defined in common.h @@ -596,7 +597,7 @@ class CSync cond.notify_all(); } - void signal_locked(UniqueLock& lk ATR_UNUSED) + void signal_locked(UniqueLock& lk SRT_ATR_UNUSED) { // EXPECTED: lk.mutex() is LOCKED. m_cond->notify_one(); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index caf882cd9..ed96306f5 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -18,7 +18,7 @@ written by // ATTRIBUTES: // -// ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) +// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) // ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) // ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode // ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. @@ -28,10 +28,8 @@ written by #ifdef __GNUG__ -#define ATR_UNUSED __attribute__((unused)) #define ATR_DEPRECATED __attribute__((deprecated)) #else -#define ATR_UNUSED #define ATR_DEPRECATED #endif #if defined(__cplusplus) && __cplusplus > 199711L @@ -719,7 +717,8 @@ struct CallbackHolder // Casting function-to-function, however, should not. Unfortunately // newer compilers disallow that, too (when a signature differs), but // then they should better use the C++11 way, much more reliable and safer. - void* (*testfn)(void*) ATR_UNUSED = (void*(*)(void*))f; + void* (*testfn)(void*) = (void*(*)(void*))f; + (void)(testfn); #endif opaque = o; fn = f; diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index 680ac6911..c8a441273 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -112,7 +112,7 @@ TEST(Core, ListenCallback) { (void)srt_cleanup(); } -int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid) +int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion, const struct sockaddr* peeraddr, const char* streamid) { using namespace std; diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index f986aa5dc..932406888 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -2260,7 +2260,7 @@ MediaPacket SrtSource::Read(size_t chunk) { static size_t counter = 1; - bool have_group ATR_UNUSED = !m_group_nodes.empty(); + bool have_group SRT_ATR_UNUSED = !m_group_nodes.empty(); bytevector data(chunk); // EXPERIMENTAL From 3c1c490715a6a6025bdda85a8ef1acf64616d8e8 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 2 Jul 2021 21:29:40 +0800 Subject: [PATCH 204/790] [tests] Fix -Wsign-compare --- test/test_buffer.cpp | 6 +++--- test/test_epoll.cpp | 4 ++-- test/test_fec_rebuilding.cpp | 6 +++--- test/test_ipv6.cpp | 2 +- test/test_seqno.cpp | 2 +- test/test_socket_options.cpp | 20 ++++++++++---------- test/test_sync.cpp | 6 +++--- test/test_utilities.cpp | 6 +++--- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 047ced7e2..d2e7b39f6 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -47,7 +47,7 @@ TEST(CRcvBuffer, FullBuffer) for (int i = 0; i < buffer_size_pkts - 1; ++i) { const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); } } @@ -109,7 +109,7 @@ TEST(CRcvBuffer, ReadData) std::array buff; const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); } @@ -148,7 +148,7 @@ TEST(CRcvBuffer, AddData) for (int i = 0; i < ack_pkts; ++i) { const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - ack_pkts + i); } diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 8445450d3..8fb36be53 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -408,7 +408,7 @@ TEST(CEPoll, HandleEpollEvent2) int result = epoll.uwait(epoll_id, fds, 1024, -1); ASSERT_EQ(result, 1); - ASSERT_EQ(fds[0].events, SRT_EPOLL_ERR); + ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR)); // Edge-triggered means that after one wait call was done, the next // call to this event should no longer report it. Now use timeout 0 @@ -529,7 +529,7 @@ TEST(CEPoll, ThreadedUpdate) int result = epoll.uwait(epoll_id, fds, 1024, -1); cerr << "Exit no longer infinite-wait by uwait, result=" << result << "\n"; ASSERT_EQ(result, 1); - ASSERT_EQ(fds[0].events, SRT_EPOLL_ERR); + ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR)); cerr << "THREAD JOIN...\n"; td.join(); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 17f7cfbc9..1cfdf8ff9 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -801,7 +801,7 @@ TEST_F(TestFECRebuilding, NoRebuild) bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up - EXPECT_EQ(provided.size(), 0); // Confirm that nothing was rebuilt + EXPECT_EQ(provided.size(), 0U); // Confirm that nothing was rebuilt /* // XXX With such a short sequence, losses will not be reported. @@ -879,8 +879,8 @@ TEST_F(TestFECRebuilding, Rebuild) const bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up - EXPECT_EQ(loss.size(), 0); - ASSERT_EQ(provided.size(), 1); + EXPECT_EQ(loss.size(), 0U); + ASSERT_EQ(provided.size(), 1U); SrtPacket& rebuilt = provided[0]; CPacket& skipped = *source[4]; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 677cae180..820288fa8 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -61,7 +61,7 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { - ASSERT_NE(fam.count(w.family()), 0) << "INVALID FAMILY"; + ASSERT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; } diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index 4a1cb0ec8..f3f50de62 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -27,7 +27,7 @@ TEST(CSeqNo, seqcmp) EXPECT_EQ(CSeqNo::seqcmp(1, 128), -127); // abs(seq1 - seq2) >= 0x3FFFFFFF : seq2 - seq1 - EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 1), 0x80000002); // -2147483646 + EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 1), int(0x80000002)); // -2147483646 EXPECT_EQ(CSeqNo::seqcmp(1, 0x7FFFFFFF), 0x7FFFFFFE); // 2147483646 } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 74a607838..d2e1da704 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -123,7 +123,7 @@ TEST_F(TestSocketOptions, LossMaxTTL) int opt_len = 0; ASSERT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS); EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the accepted socket"; - EXPECT_EQ(opt_len, sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the accepted socket"; + EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the accepted socket"; SRT_TRACEBSTATS stats; EXPECT_EQ(srt_bstats(accepted_sock, &stats, 0), SRT_SUCCESS); @@ -131,7 +131,7 @@ TEST_F(TestSocketOptions, LossMaxTTL) ASSERT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS); EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the listener socket"; - EXPECT_EQ(opt_len, sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the listener socket"; + EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the listener socket"; ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); } @@ -272,7 +272,7 @@ TEST_F(TestSocketOptions, StreamIDOdd) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_odd); - EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(size_t(buffer_len), sid_odd.size()); EXPECT_EQ(strlen(buffer), sid_odd.size()); StartListener(); @@ -283,7 +283,7 @@ TEST_F(TestSocketOptions, StreamIDOdd) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(size_t(buffer_len), sid_odd.size()); EXPECT_EQ(strlen(buffer), sid_odd.size()); ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); @@ -301,7 +301,7 @@ TEST_F(TestSocketOptions, StreamIDEven) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_even); - EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(size_t(buffer_len), sid_even.size()); EXPECT_EQ(strlen(buffer), sid_even.size()); StartListener(); @@ -312,7 +312,7 @@ TEST_F(TestSocketOptions, StreamIDEven) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(size_t(buffer_len), sid_even.size()); EXPECT_EQ(strlen(buffer), sid_even.size()); ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); @@ -337,7 +337,7 @@ TEST_F(TestSocketOptions, StreamIDAlmostFull) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_amost_full); - EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_amost_full.size()); EXPECT_EQ(strlen(buffer), sid_amost_full.size()); StartListener(); @@ -348,7 +348,7 @@ TEST_F(TestSocketOptions, StreamIDAlmostFull) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_amost_full.size()); EXPECT_EQ(strlen(buffer), sid_amost_full.size()); EXPECT_EQ(buffer[sid_amost_full.size()-1], 'z'); @@ -373,7 +373,7 @@ TEST_F(TestSocketOptions, StreamIDFull) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_full); - EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_full.size()); EXPECT_EQ(strlen(buffer), sid_full.size()); StartListener(); @@ -384,7 +384,7 @@ TEST_F(TestSocketOptions, StreamIDFull) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_full.size()); EXPECT_EQ(strlen(buffer), sid_full.size()); EXPECT_EQ(buffer[sid_full.size()-1], 'z'); diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 4967fbf86..6fa59c137 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -461,7 +461,7 @@ TEST(SyncEvent, WaitForTwoNotifyOne) // Now exactly one waiting thread should become ready // Error if: 0 (none ready) or 2 (both ready, while notify_one was used) - ASSERT_EQ(notified_clients.size(), 1); + ASSERT_EQ(notified_clients.size(), 1U); const int ready = notified_clients[0]; const int not_ready = (ready + 1) % 2; @@ -611,7 +611,7 @@ TEST(Sync, FormatTime) const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6,}) \\[STDY\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); - EXPECT_LE(sm.size(), 6); + EXPECT_LE(sm.size(), 6U); if (sm.size() != 6 && sm.size() != 5) return 0; @@ -655,7 +655,7 @@ TEST(Sync, FormatTimeSys) const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYST\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); - EXPECT_EQ(sm.size(), 5); + EXPECT_EQ(sm.size(), 5U); if (sm.size() != 5) return 0; diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp index 7deabe736..0ee56771c 100644 --- a/test/test_utilities.cpp +++ b/test/test_utilities.cpp @@ -219,7 +219,7 @@ TEST(ConfigString, Setting) StringStorage s; EXPECT_TRUE(s.empty()); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); EXPECT_EQ(s.str(), std::string()); char example_ac1[] = "example_long"; @@ -246,7 +246,7 @@ TEST(ConfigString, Setting) EXPECT_EQ(s.size(), sizeof (example_ac3)-1); EXPECT_TRUE(s.set(example_ace, sizeof (example_ace)-1)); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); string example_s1 = "example_long"; string example_s2 = "short"; @@ -268,6 +268,6 @@ TEST(ConfigString, Setting) EXPECT_EQ(s.size(), example_s3.size()); EXPECT_TRUE(s.set(example_se)); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); EXPECT_TRUE(s.empty()); } From e8d890caf38c0ee851bf6c5a76da1bca407bd5db Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 16:18:53 +0200 Subject: [PATCH 205/790] [core] Moved mutex from CSndQueue to CSndUList Co-authored-by: Sektor van Skijlen --- CMakeLists.txt | 2 +- srtcore/queue.cpp | 51 +++++++++++++++++++++++++++-------------------- srtcore/queue.h | 35 +++++++++++++++----------------- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d360671..c14c81c55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,7 +129,7 @@ option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX librari option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) option(ENABLE_CODE_COVERAGE "Enable code coverage reporting" OFF) -option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV /temporary fix for #729/" OFF) +option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" OFF) option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" OFF) option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index b9d7ed02e..f17ad5745 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -255,20 +255,20 @@ void srt::CUnitQueue::makeUnitGood(CUnit* unit) unit->m_iFlag = CUnit::GOOD; } -srt::CSndUList::CSndUList() +srt::CSndUList::CSndUList(sync::CTimer* pTimer) : m_pHeap(NULL) , m_iArrayLength(512) , m_iLastEntry(-1) , m_ListLock() - , m_pWindowLock(NULL) - , m_pWindowCond(NULL) - , m_pTimer(NULL) + , m_pTimer(pTimer) { + setupCond(m_ListCond, "CSndUListCond"); m_pHeap = new CSNode*[m_iArrayLength]; } srt::CSndUList::~CSndUList() { + releaseCond(m_ListCond); delete[] m_pHeap; } @@ -305,7 +305,7 @@ int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) if (-1 == m_iLastEntry) return -1; - // no pop until the next schedulled time + // no pop until the next scheduled time if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) return -1; @@ -342,7 +342,6 @@ int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) void srt::CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); - remove_(u); } @@ -356,6 +355,21 @@ steady_clock::time_point srt::CSndUList::getNextProcTime() return m_pHeap[0]->m_tsTimeStamp; } +void srt::CSndUList::waitNonEmpty() const +{ + UniqueLock listguard(m_ListLock); + if (m_iLastEntry >= 0) + return; + + m_ListCond.wait(listguard); +} + +void srt::CSndUList::signalInterrupt() const +{ + ScopedLock listguard(m_ListLock); + m_ListCond.notify_all(); +} + void srt::CSndUList::realloc_() { CSNode** temp = NULL; @@ -420,7 +434,8 @@ void srt::CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const // first entry, activate the sending queue if (0 == m_iLastEntry) { - CSync::lock_signal(*m_pWindowCond, *m_pWindowLock); + // m_ListLock is assumed to be locked. + m_ListCond.notify_all(); } } @@ -468,10 +483,8 @@ srt::CSndQueue::CSndQueue() : m_pSndUList(NULL) , m_pChannel(NULL) , m_pTimer(NULL) - , m_WindowCond() , m_bClosing(false) { - setupCond(m_WindowCond, "Window"); } srt::CSndQueue::~CSndQueue() @@ -483,14 +496,14 @@ srt::CSndQueue::~CSndQueue() m_pTimer->interrupt(); } - CSync::lock_signal(m_WindowCond, m_WindowLock); + // Unblock CSndQueue worker thread if it is waiting. + m_pSndUList->signalInterrupt(); if (m_WorkerThread.joinable()) { HLOGC(rslog.Debug, log << "SndQueue: EXIT"); m_WorkerThread.join(); } - releaseCond(m_WindowCond); delete m_pSndUList; } @@ -510,12 +523,9 @@ int srt::CSndQueue::m_counter = 0; void srt::CSndQueue::init(CChannel* c, CTimer* t) { - m_pChannel = c; - m_pTimer = t; - m_pSndUList = new CSndUList; - m_pSndUList->m_pWindowLock = &m_WindowLock; - m_pSndUList->m_pWindowCond = &m_WindowCond; - m_pSndUList->m_pTimer = m_pTimer; + m_pChannel = c; + m_pTimer = t; + m_pSndUList = new CSndUList(t); #if ENABLE_LOGGING ++m_counter; @@ -575,14 +585,11 @@ void* srt::CSndQueue::worker(void* param) self->m_WorkerStats.lNotReadyTs++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ - UniqueLock windlock(self->m_WindowLock); - CSync windsync(self->m_WindowCond, windlock); - // wait here if there is no sockets with data to be sent THREAD_PAUSED(); - if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) + if (!self->m_bClosing) { - windsync.wait(); + self->m_pSndUList->waitNonEmpty(); #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lCondWait++; diff --git a/srtcore/queue.h b/srtcore/queue.h index ee05440c8..77256c3fc 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -157,10 +157,8 @@ struct CSNode class CSndUList { - friend class CSndQueue; - public: - CSndUList(); + CSndUList(sync::CTimer* pTimer); ~CSndUList(); public: @@ -175,30 +173,32 @@ class CSndUList /// Update the timestamp of the UDT instance on the list. /// @param [in] u pointer to the UDT instance /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. /// @param [out] addr destination address of the next packet /// @param [out] pkt the next packet to be sent /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); /// Remove UDT instance from the list. /// @param [in] u pointer to the UDT instance - - void remove(const CUDT* u); + void remove(const CUDT* u);// EXCLUDES(m_ListLock); /// Retrieve the next scheduled processing time. /// @return Scheduled processing time of the first UDT socket in the list. - sync::steady_clock::time_point getNextProcTime(); + /// Wait for the list to become non empty. + void waitNonEmpty() const; + + /// Signal to stop waiting in waitNonEmpty(). + void signalInterrupt() const; + private: /// Doubles the size of the list. /// - void realloc_(); + void realloc_();// REQUIRES(m_ListLock); /// Insert a new UDT instance into the list with realloc if required. /// @@ -211,21 +211,21 @@ class CSndUList /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u); + void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u);// REQUIRES(m_ListLock); + /// Removes CUDT entry from the list. + /// If the last entry is removed, calls sync::CTimer::interrupt(). void remove_(const CUDT* u); private: CSNode** m_pHeap; // The heap array int m_iArrayLength; // physical length of the array - int m_iLastEntry; // position of last entry on the heap array + int m_iLastEntry; // position of last entry on the heap array or -1 if empty. - sync::Mutex m_ListLock; + mutable sync::Mutex m_ListLock; // Protects the list (m_pHeap, m_iArrayLength, m_iLastEntry). + mutable sync::Condition m_ListCond; - sync::Mutex* m_pWindowLock; - sync::Condition* m_pWindowCond; - - sync::CTimer* m_pTimer; + sync::CTimer* const m_pTimer; private: CSndUList(const CSndUList&); @@ -462,9 +462,6 @@ class CSndQueue CChannel* m_pChannel; // The UDP channel for data sending srt::sync::CTimer* m_pTimer; // Timing facility - srt::sync::Mutex m_WindowLock; - srt::sync::Condition m_WindowCond; - srt::sync::atomic m_bClosing; // closing the worker #if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker From e9c550b0e7c26ff80d7ad80d2ad2ab916d249c08 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 17:09:27 +0200 Subject: [PATCH 206/790] [core] Refactor CSndUList: pop CUDT, not a packet --- srtcore/core.cpp | 4 +-- srtcore/queue.cpp | 82 ++++++++++++++++++++++++++--------------------- srtcore/queue.h | 11 +++---- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 15a138a3a..14dd2c888 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7974,14 +7974,14 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) #endif // insert this socket to snd list if it is not on the list yet - m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + const steady_clock::time_point currtime = steady_clock::now(); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE, currtime); if (m_config.bSynSending) { CSync::lock_signal(m_SendBlockCond, m_SendBlockLock); } - const steady_clock::time_point currtime = steady_clock::now(); // record total time used for sending enterCS(m_StatsLock); m_stats.sndDuration += count_microseconds(currtime - m_stats.sndDurationCounter); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index f17ad5745..8cefd276d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -272,7 +272,7 @@ srt::CSndUList::~CSndUList() delete[] m_pHeap; } -void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) +void srt::CSndUList::update(const CUDT* u, EReschedule reschedule, sync::steady_clock::time_point ts) { ScopedLock listguard(m_ListLock); @@ -285,58 +285,33 @@ void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) if (n->m_iHeapLoc == 0) { - n->m_tsTimeStamp = steady_clock::now(); + n->m_tsTimeStamp = ts; m_pTimer->interrupt(); return; } remove_(u); - insert_norealloc_(steady_clock::now(), u); + insert_norealloc_(ts, u); return; } - insert_(steady_clock::now(), u); + insert_(ts, u); } -int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) +srt::CUDT* srt::CSndUList::pop() { ScopedLock listguard(m_ListLock); if (-1 == m_iLastEntry) - return -1; + return NULL; // no pop until the next scheduled time if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) - return -1; + return NULL; CUDT* u = m_pHeap[0]->m_pUDT; remove_(u); - -#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " - - HLOGC(qslog.Debug, - log << "SND:pop: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) - << UST(Connecting) << UST(Connected) << UST(Closing) << UST(Shutdown) << UST(Broken) << UST(PeerHealth) - << UST(Opened)); -#undef UST - - if (!u->m_bConnected || u->m_bBroken) - return -1; - - // pack a packet from the socket - const std::pair res_time = u->packData((w_pkt)); - - if (res_time.first <= 0) - return -1; - - w_addr = u->m_PeerAddr; - - // insert a new entry, ts is the next processing time - const steady_clock::time_point send_time = res_time.second; - if (!is_zero(send_time)) - insert_norealloc_(send_time, u); - - return 1; + return u; } void srt::CSndUList::remove(const CUDT* u) @@ -630,18 +605,51 @@ void* srt::CSndQueue::worker(void* param) } THREAD_RESUMED(); - // it is time to send the next pkt - sockaddr_any addr; - CPacket pkt; - if (self->m_pSndUList->pop((addr), (pkt)) < 0) + // Get a socket with a send request if any. + CUDT* u = self->m_pSndUList->pop(); + if (u == NULL) { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyPop++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ continue; + } + +#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " + HLOGC(qslog.Debug, + log << "CSndQueue: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) + << UST(Connecting) << UST(Connected) << UST(Closing) << UST(Shutdown) << UST(Broken) << UST(PeerHealth) + << UST(Opened)); +#undef UST + if (!u->m_bConnected || u->m_bBroken) + { #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lNotReadyPop++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + continue; } + // pack a packet from the socket + CPacket pkt; + const std::pair res_time = u->packData((pkt)); + + // Check if payload size is invalid. + if (res_time.first <= 0) + { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyPop++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + continue; + } + + const sockaddr_any addr = u->m_PeerAddr; + // Insert a new entry, send_time is the next processing time. + // TODO: maybe reschedule by taking the smaller time? + const steady_clock::time_point send_time = res_time.second; + if (!is_zero(send_time)) + self->m_pSndUList->update(u, CSndUList::DONT_RESCHEDULE, send_time); + HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info()); self->m_pChannel->sendto(addr, pkt); diff --git a/srtcore/queue.h b/srtcore/queue.h index 77256c3fc..2b7408747 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -173,13 +173,12 @@ class CSndUList /// Update the timestamp of the UDT instance on the list. /// @param [in] u pointer to the UDT instance /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); + /// @param [in] ts the next time to trigger sending logic on the CUDT + void update(const CUDT* u, EReschedule reschedule, sync::steady_clock::time_point ts = sync::steady_clock::now()); - /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. - /// @param [out] addr destination address of the next packet - /// @param [out] pkt the next packet to be sent - /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); + /// Retrieve the next (in time) socket from the heap to process its sending request. + /// @return a pointer to CUDT instance to process next. + CUDT* pop(); /// Remove UDT instance from the list. /// @param [in] u pointer to the UDT instance From 96a41db2e0a7f53f9b16a0f823663892afadb1b3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Jul 2021 16:47:07 +0200 Subject: [PATCH 207/790] [core] CSndUList::update: reschedule if earlier time --- srtcore/queue.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8cefd276d..2b0fd2c74 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -280,7 +280,10 @@ void srt::CSndUList::update(const CUDT* u, EReschedule reschedule, sync::steady_ if (n->m_iHeapLoc >= 0) { - if (!reschedule) // EReschedule to bool conversion, predicted. + if (reschedule == DONT_RESCHEDULE) + return; + + if (n->m_tsTimeStamp <= ts) return; if (n->m_iHeapLoc == 0) @@ -644,11 +647,9 @@ void* srt::CSndQueue::worker(void* param) } const sockaddr_any addr = u->m_PeerAddr; - // Insert a new entry, send_time is the next processing time. - // TODO: maybe reschedule by taking the smaller time? - const steady_clock::time_point send_time = res_time.second; - if (!is_zero(send_time)) - self->m_pSndUList->update(u, CSndUList::DONT_RESCHEDULE, send_time); + const steady_clock::time_point next_send_time = res_time.second; + if (!is_zero(next_send_time)) + self->m_pSndUList->update(u, CSndUList::DO_RESCHEDULE, next_send_time); HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info()); self->m_pChannel->sendto(addr, pkt); From 65ae2575798595d209f4d57452b0b990e7295d55 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 20 Jul 2021 11:56:49 +0200 Subject: [PATCH 208/790] [core] Added missing RCV buffer lock in CUDT::receiveBuffer(..). Co-authored-by: Maxim Sharabayko --- srtcore/core.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 14dd2c888..65b51e850 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6108,7 +6108,9 @@ int srt::CUDT::receiveBuffer(char *data, int len) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } + enterCS(m_RcvBufferLock); const int res = m_pRcvBuffer->readBuffer(data, len); + leaveCS(m_RcvBufferLock); /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -7056,7 +7058,9 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo } unitsize = int((torecv > block) ? block : torecv); + enterCS(m_RcvBufferLock); recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); + leaveCS(m_RcvBufferLock); if (recvsize > 0) { From ec24e15d50778844989676b4d36476d1c88f7bd5 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 21 Jul 2021 11:48:23 +0800 Subject: [PATCH 209/790] [core] Replace hacky static_assert by SRT_STATIC_ASSERT Remove local static SRTDATA_MAXSIZE since there is another one in global namespace. --- srtcore/core.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 65b51e850..75c0171ae 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1208,16 +1208,9 @@ void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) CPacket srtpkt; int32_t srtcmd = (int32_t)cmd; - static const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ / sizeof(int32_t); - - // This is in order to issue a compile error if the SRT_CMD_MAXSZ is - // too small to keep all the data. As this is "static const", declaring - // an array of such specified size in C++ isn't considered VLA. - static const int SRTDATA_SIZE = SRTDATA_MAXSIZE >= SRT_HS_E_SIZE ? SRTDATA_MAXSIZE : -1; - - // This will be effectively larger than SRT_HS_E_SIZE, but it will be also used - // for incoming data. We have a guarantee that it won't be larger than SRTDATA_MAXSIZE. - uint32_t srtdata[SRTDATA_SIZE]; + SRT_STATIC_ASSERT(SRTDATA_MAXSIZE >= SRT_HS_E_SIZE, "SRT_CMD_MAXSZ is too small to hold all the data"); + // This will be effectively larger than SRT_HS_E_SIZE, but it will be also used for incoming data. + uint32_t srtdata[SRTDATA_MAXSIZE]; size_t srtlen = 0; @@ -1233,7 +1226,7 @@ void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) { case SRT_CMD_HSREQ: case SRT_CMD_HSRSP: - srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_SIZE); + srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_MAXSIZE); break; case SRT_CMD_KMREQ: // Sender From 1df29db15d671c91ea8e1dc30c263cd39e8138ad Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 21 Jul 2021 11:56:54 +0800 Subject: [PATCH 210/790] [core] use sizeof(uint32_t) in SRTDATA_MAXSIZE Since all usecase of SRTDATA_MAXSIZE are array of uint32_t. --- srtcore/crypto.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 4e067678b..558f229f2 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -48,7 +48,7 @@ namespace srt const size_t SRT_KMR_KMSTATE = 0; #define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */ -const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(int32_t); +const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t); enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; From a34aa086060071e024076cec05f1b24ec7834416 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 22 Jul 2021 12:17:22 +0800 Subject: [PATCH 211/790] [core] Set CLOEXEC for epoll on Linux epoll_create1() was added to the kernel in version 2.6.27. Library support is provided in glibc starting with version 2.9 which was released at the year of 2008. --- srtcore/epoll.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 548418f88..5755c9dff 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -108,7 +108,11 @@ int CEPoll::create(CEPollDesc** pout) int localid = 0; #ifdef LINUX - localid = epoll_create(1024); + int flags = 0; +#if ENABLE_SOCK_CLOEXEC + flags |= EPOLL_CLOEXEC; +#endif + localid = epoll_create1(flags); /* Possible reasons of -1 error: EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered. ENFILE: The system limit on the total number of open files has been reached. From 2ca53138a3669c192d2f9f8166c9749941f10dae Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Jul 2021 15:07:01 +0200 Subject: [PATCH 212/790] [core] Added Clang TSA attributes. (#2000) - Added CMake build option ENABLE_CLANG_TSA. - srt::sync::UniqueLock now may throw an exception in unlock and lock. - MSVC SAL attributes are partially added. --- CMakeLists.txt | 7 +++ srtcore/logging.h | 3 + srtcore/platform_sys.h | 2 +- srtcore/srt.h | 2 +- srtcore/srt_attr_defs.h | 119 ++++++++++++++++++++++++++++++++++++++++ srtcore/sync.h | 46 ++++++++++------ srtcore/sync_posix.cpp | 21 ++++--- srtcore/udt.h | 4 +- 8 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 srtcore/srt_attr_defs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c14c81c55..f210ff34f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,8 @@ option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potenti option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) + set(TARGET_srt "srt" CACHE STRING "The name for the SRT library") # Use application-defined group reader @@ -634,6 +636,11 @@ if (ENABLE_THREAD_CHECK) ) endif() +if (ENABLE_CLANG_TSA) + list(APPEND SRT_EXTRA_CFLAGS "-Wthread-safety") + message(STATUS "Clang TSA: Enabled") +endif() + if (ENABLE_PROFILE) if (HAVE_COMPILER_GNU_COMPAT) # They are actually cflags, not definitions, but CMake is stupid enough. diff --git a/srtcore/logging.h b/srtcore/logging.h index 8767f2aa7..5669c8f45 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -131,7 +131,10 @@ struct LogConfig { } + SRT_ATTR_ACQUIRE(mutex) void lock() { mutex.lock(); } + + SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } }; diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index 9fa2c7d32..79760080e 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -41,7 +41,7 @@ #include #include #if defined(_MSC_VER) - #pragma warning(disable:4251) + #pragma warning(disable: 4251 26812) #endif #else diff --git a/srtcore/srt.h b/srtcore/srt.h index 5d3349a48..496112249 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -963,7 +963,7 @@ typedef struct SRT_EPOLL_EVENT_STR int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR #ifdef __cplusplus SRT_EPOLL_EVENT_STR(SRTSOCKET s, int ev): fd(s), events(ev) {} - SRT_EPOLL_EVENT_STR() {} // NOTE: allows singular values, no init. + SRT_EPOLL_EVENT_STR(): fd(-1), events(0) {} // NOTE: allows singular values, no init. #endif } SRT_EPOLL_EVENT; SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h new file mode 100644 index 000000000..24bc8bc84 --- /dev/null +++ b/srtcore/srt_attr_defs.h @@ -0,0 +1,119 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2019 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +/***************************************************************************** +The file contains various planform and compiler dependent attribute definitions +used by SRT library internally. + +1. Attributes for thread safety analysis + - Clang (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). + - Other compilers: none. + + *****************************************************************************/ + +#ifndef INC_SRT_ATTR_DEFS_H +#define INC_SRT_ATTR_DEFS_H + +#if _MSC_VER >= 1920 +// In case of MSVC these attributes have to preceed the attributed objects (variable, function). +// E.g. SRT_ATTR_GUARDED_BY(mtx) int object; +// It is tricky to annotate e.g. the following function, as clang complaints it does not know 'm'. +// SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) +// inline void enterCS(Mutex& m) { m.lock(); } +#define SRT_ATTR_CAPABILITY(expr) +#define SRT_ATTR_SCOPED_CAPABILITY +#define SRT_ATTR_GUARDED_BY(expr) _Guarded_by_(expr) +#define SRT_ATTR_PT_GUARDED_BY(expr) +#define SRT_ATTR_ACQUIRED_BEFORE(...) +#define SRT_ATTR_ACQUIRED_AFTER(...) +#define SRT_ATTR_REQUIRES(expr) _Requires_lock_held_(expr) +#define SRT_ATTR_REQUIRES_SHARED(...) +#define SRT_ATTR_ACQUIRE(expr) _Acquires_nonreentrant_lock_(expr) +#define SRT_ATTR_ACQUIRE_SHARED(...) +#define SRT_ATTR_RELEASE(expr) _Releases_lock_(expr) +#define SRT_ATTR_RELEASE_SHARED(...) +#define SRT_ATTR_RELEASE_GENERIC(...) +#define SRT_ATTR_TRY_ACQUIRE(...) _Acquires_nonreentrant_lock_(expr) +#define SRT_ATTR_TRY_ACQUIRE_SHARED(...) +#define SRT_ATTR_EXCLUDES(...) +#define SRT_ATTR_ASSERT_CAPABILITY(expr) +#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) +#define SRT_ATTR_RETURN_CAPABILITY(x) +#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS +#else + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define SRT_ATTR_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define SRT_ATTR_SCOPED_CAPABILITY \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +#define SRT_ATTR_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#define SRT_ATTR_PT_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +#define SRT_ATTR_ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define SRT_ATTR_REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define SRT_ATTR_REQUIRES_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE_GENERIC(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) + +#define SRT_ATTR_TRY_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define SRT_ATTR_TRY_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_EXCLUDES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +#define SRT_ATTR_ASSERT_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +#define SRT_ATTR_RETURN_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif // not _MSC_VER + +#endif // INC_SRT_ATTR_DEFS_H diff --git a/srtcore/sync.h b/srtcore/sync.h index 7da6952f7..081cb41e4 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -53,6 +53,7 @@ #include "srt.h" #include "utilities.h" +#include "srt_attr_defs.h" class CUDTException; // defined in common.h @@ -379,7 +380,7 @@ using ScopedLock = lock_guard; /// Mutex is a class wrapper, that should mimic the std::chrono::mutex class. /// At the moment the extra function ref() is temporally added to allow calls /// to pthread_cond_timedwait(). Will be removed by introducing CEvent. -class Mutex +class SRT_ATTR_CAPABILITY("mutex") Mutex { friend class SyncEvent; @@ -388,11 +389,11 @@ class Mutex ~Mutex(); public: - int lock(); - int unlock(); + int lock() SRT_ATTR_ACQUIRE(); + int unlock() SRT_ATTR_RELEASE(); /// @return true if the lock was acquired successfully, otherwise false - bool try_lock(); + bool try_lock() SRT_ATTR_TRY_ACQUIRE(true); // TODO: To be removed with introduction of the CEvent. pthread_mutex_t& ref() { return m_mutex; } @@ -402,10 +403,13 @@ class Mutex }; /// A pthread version of std::chrono::scoped_lock (or lock_guard for C++11) -class ScopedLock +class SRT_ATTR_SCOPED_CAPABILITY ScopedLock { public: + SRT_ATTR_ACQUIRE(m) ScopedLock(Mutex& m); + + SRT_ATTR_RELEASE() ~ScopedLock(); private: @@ -413,17 +417,25 @@ class ScopedLock }; /// A pthread version of std::chrono::unique_lock -class UniqueLock +class SRT_ATTR_SCOPED_CAPABILITY UniqueLock { friend class SyncEvent; public: + SRT_ATTR_ACQUIRE(m_Mutex) UniqueLock(Mutex &m); + + SRT_ATTR_RELEASE(m_Mutex) ~UniqueLock(); public: + SRT_ATTR_ACQUIRE(m_Mutex) void lock(); + + SRT_ATTR_RELEASE(m_Mutex) void unlock(); + + SRT_ATTR_RETURN_CAPABILITY(m_Mutex) Mutex* mutex(); // reflects C++11 unique_lock::mutex() private: @@ -432,26 +444,28 @@ class UniqueLock }; #endif // ENABLE_STDCXX_SYNC -inline void enterCS(Mutex& m) { m.lock(); } -inline bool tryEnterCS(Mutex& m) { return m.try_lock(); } -inline void leaveCS(Mutex& m) { m.unlock(); } +inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); } + +inline bool tryEnterCS(Mutex& m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } + +inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); } class InvertedLock { - Mutex *m_pMutex; + Mutex& m_mtx; - public: +public: + SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) InvertedLock(Mutex& m) - : m_pMutex(&m) + : m_mtx(m) { - leaveCS(*m_pMutex); + m_mtx.unlock(); } + SRT_ATTR_ACQUIRE(m_mtx) ~InvertedLock() { - if (!m_pMutex) - return; - enterCS(*m_pMutex); + m_mtx.lock(); } }; diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 0972dd731..960a1dfad 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -244,22 +244,27 @@ srt::sync::UniqueLock::UniqueLock(Mutex& m) srt::sync::UniqueLock::~UniqueLock() { - unlock(); + if (m_iLocked == 0) + { + unlock(); + } } void srt::sync::UniqueLock::lock() { - if (m_iLocked == -1) - m_iLocked = m_Mutex.lock(); + if (m_iLocked != -1) + throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0); + + m_iLocked = m_Mutex.lock(); } void srt::sync::UniqueLock::unlock() { - if (m_iLocked == 0) - { - m_Mutex.unlock(); - m_iLocked = -1; - } + if (m_iLocked != 0) + throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0); + + m_Mutex.unlock(); + m_iLocked = -1; } srt::sync::Mutex* srt::sync::UniqueLock::mutex() diff --git a/srtcore/udt.h b/srtcore/udt.h index b1af441b2..60bc8d07d 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -70,10 +70,10 @@ modified by #include "srt.h" /* -* SRT_ENABLE_THREADCHECK (THIS IS SET IN MAKEFILE NOT HERE) +* SRT_ENABLE_THREADCHECK IS SET IN MAKEFILE, NOT HERE */ #if defined(SRT_ENABLE_THREADCHECK) -#include +#include "threadcheck.h" #else #define THREAD_STATE_INIT(name) #define THREAD_EXIT() From d5d4b18f3efc59617b47e8dc6ca9b40b62872e93 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 26 Jul 2021 09:24:22 +0200 Subject: [PATCH 213/790] [docs] Fixed a link in Configuration Guidelines --- docs/API/configuration-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index f0566d7d0..1e6d2111e 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -6,7 +6,7 @@ The receiver buffer can be configured with the [`SRTO_RCVBUF`](./API-socket-opti Buffer size in bytes is expected to be passed in the `optval` argument of the `srt_setsockopt(..)` function. However, internally the value will be converted into the number of packets stored in the receiver buffer. -The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`]((./API-socket-options.md#SRTO_FC) socket option. +The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`](./API-socket-options.md#SRTO_FC) socket option. See issue [#700](https://github.com/Haivision/srt/issues/700). The default flow control window size is 25600 packets. It is approximately: From 589f103926e9297fac4cd7f2c1e9dbdde9d8925c Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 5 Jul 2021 18:46:21 +0800 Subject: [PATCH 214/790] [core] Move CSrtConfigSetter templates to cpp file They are only used by socketconfig.cpp, socketconfig.h is included by multiple files. --- srtcore/socketconfig.cpp | 818 ++++++++++++++++++++++++++++++++++++++ srtcore/socketconfig.h | 829 --------------------------------------- 2 files changed, 818 insertions(+), 829 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index e50bb08f5..8cb06a08f 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -54,6 +54,824 @@ written by extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); +typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); + +template +struct CSrtConfigSetter +{ + static setter_function set; +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int ival = cast_optval(optval, optlen); + if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iMSS = ival; + + // Packet size cannot be greater than UDP buffer size + if (co.iMSS > co.iUDPSndBufSize) + co.iMSS = co.iUDPSndBufSize; + if (co.iMSS > co.iUDPRcvBufSize) + co.iMSS = co.iUDPRcvBufSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + const int fc = cast_optval(optval, optlen); + if (fc < co.DEF_MIN_FLIGHT_PKT) + { + LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + co.iFlightFlagSize = fc; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int bs = cast_optval(optval, optlen); + if (bs <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Mimimum recv buffer size is 32 packets + const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; + + if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) + co.iRcvBufSize = val / mssin_size; + else + co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; + + // recv buffer MUST not be greater than FC size + if (co.iRcvBufSize > co.iFlightFlagSize) + co.iRcvBufSize = co.iFlightFlagSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.Linger = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iUDPSndBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iUDPRcvBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bRendezvous = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndTimeOut = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvTimeOut = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bSynSending = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bSynRecving = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bReuseAddr = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.llMaxBW = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int val = cast_optval(optval, optlen); + if (!(val == -1) && !((val >= 1) && (val <= 255))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.iIpTTL = cast_optval(optval); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iIpToS = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_BINDTODEVICE + using namespace std; + using namespace srt_logging; + + string val; + if (optlen == -1) + val = (const char *)optval; + else + val.assign((const char *)optval, optlen); + if (val.size() >= IFNAMSIZ) + { + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.sBindToDevice = val; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.llInputBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.llMinInputBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int32_t val = cast_optval(optval, optlen); + if (val < 5 || val > 100) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.iOverheadBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bDataSender = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bTSBPD = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + co.iPeerLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bTLPktDrop = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndDropDelay = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + // Password must be 10-80 characters. + // Or it can be empty to clear the password. + if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + memset(&co.CryptoSecret, 0, sizeof(co.CryptoSecret)); + co.CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; + co.CryptoSecret.len = (optlen <= (int)sizeof(co.CryptoSecret.str) ? optlen : (int)sizeof(co.CryptoSecret.str)); + memcpy((co.CryptoSecret.str), optval, co.CryptoSecret.len); +#else + (void)co; // prevent warning + (void)optval; + if (optlen == 0) + return; // Allow to set empty passphrase if no encryption supported. + + LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + const int v = cast_optval(optval, optlen); + int const allowed[4] = { + 0, // Default value, if this results for initiator, defaults to 16. See below. + 16, // AES-128 + 24, // AES-192 + 32 // AES-256 + }; + const int *const allowed_end = allowed + 4; + if (std::find(allowed, allowed_end, v) == allowed_end) + { + LOGC(aclog.Error, + log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Note: This works a little different in HSv4 and HSv5. + + // HSv4: + // The party that is set SRTO_SENDER will send KMREQ, and it will + // use default value 16, if SRTO_PBKEYLEN is the default value 0. + // The responder that receives KMRSP has nothing to say about + // PBKEYLEN anyway and it will take the length of the key from + // the initiator (sender) as a good deal. + // + // HSv5: + // The initiator (independently on the sender) will send KMREQ, + // and as it should be the sender to decide about the PBKEYLEN. + // Your application should do the following then: + // 1. The sender should set PBKEYLEN to the required value. + // 2. If the sender is initiator, it will create the key using + // its preset PBKEYLEN (or default 16, if not set) and the + // receiver-responder will take it as a good deal. + // 3. Leave the PBKEYLEN value on the receiver as default 0. + // 4. If sender is responder, it should then advertise the PBKEYLEN + // value in the initial handshake messages (URQ_INDUCTION if + // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case + // of rendezvous, as it is the matter of luck who of them will + // eventually become the initiator). This way the receiver + // being an initiator will set iSndCryptoKeyLen before setting + // up KMREQ for sending to the sender-responder. + // + // Note that in HSv5 if both sides set PBKEYLEN, the responder + // wins, unless the initiator is a sender (the effective PBKEYLEN + // will be the one advertised by the responder). If none sets, + // PBKEYLEN will default to 16. + + co.iSndCryptoKeyLen = v; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bRcvNakReport = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + using namespace srt::sync; + co.tdConnTimeOut = milliseconds_from(val); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bDriftTracer = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iMaxReorderTolerance = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.uSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.uMinimumPeerSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.sStreamName.set((const char*)optval, optlen); + } +}; + +template<> +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); + + // Translate alias + if (val == "vod") + val = "file"; + + bool res = SrtCongestion::exists(val); + if (!res) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.sCongestion.set(val); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bMessageAPI = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + const int val = cast_optval(optval, optlen); + if (val < 0) + { + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (val > SRT_LIVE_MAX_PLSIZE) + { + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (!co.sPacketFilterConfig.empty()) + { + // This means that the filter might have been installed before, + // and the fix to the maximum payload size was already applied. + // This needs to be checked now. + srt::SrtFilterConfig fc; + if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) + { + // Break silently. This should not happen + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (size_t(val) > efc_max_payload_size) + { + LOGC(aclog.Error, + log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size + << " required for packet filter header"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + co.zExpPayloadSize = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + // XXX Note that here the configuration for SRTT_LIVE + // is the same as DEFAULT VALUES for these fields set + // in CUDT::CUDT. + switch (cast_optval(optval, optlen)) + { + case SRTT_LIVE: + // Default live options: + // - tsbpd: on + // - latency: 120ms + // - linger: off + // - congctl: live + // - extraction method: message (reading call extracts one message) + co.bTSBPD = true; + co.iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; + co.iPeerLatency = 0; + co.bTLPktDrop = true; + co.iSndDropDelay = 0; + co.bMessageAPI = true; + co.bRcvNakReport = true; + co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + co.Linger.l_onoff = 0; + co.Linger.l_linger = 0; + co.sCongestion.set("live", 4); + break; + + case SRTT_FILE: + // File transfer mode: + // - tsbpd: off + // - latency: 0 + // - linger: on + // - congctl: file (original UDT congestion control) + // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) + co.bTSBPD = false; + co.iRcvLatency = 0; + co.iPeerLatency = 0; + co.bTLPktDrop = false; + co.iSndDropDelay = -1; + co.bMessageAPI = false; + co.bRcvNakReport = false; + co.zExpPayloadSize = 0; // use maximum + co.Linger.l_onoff = 1; + co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; + co.sCongestion.set("file", 4); + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iGroupConnect = cast_optval(optval, optlen); + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value + co.uKmRefreshRatePkt = (unsigned) val; + + if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) + return; // Both values are default + + const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; + const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + + if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) + { + co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; + LOGC(aclog.Warn, + log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" + << std::hex << co.uKmPreAnnouncePkt); + } + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; + const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + if (km_preanno > (kmref - 1) / 2) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + << " - OPTION REJECTED."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.uKmPreAnnouncePkt = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bEnforcedEnc = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerIdleTimeout = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iIpV6Only = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + std::string arg((const char*)optval, optlen); + // Parse the configuration string prematurely + srt::SrtFilterConfig fc; + srt::PacketFilter::Factory* fax = 0; + if (!srt::ParseFilterConfig(arg, (fc), (&fax))) + { + LOGC(aclog.Error, + log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " + "FILTERTYPE (" + << fc.type << ") must be installed (or builtin)"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + std::string error; + if (!fax->verifyConfig(fc, (error))) + { + LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect config: " << error); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (co.zExpPayloadSize > efc_max_payload_size) + { + LOGC(aclog.Warn, + log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + << efc_max_payload_size << " bytes"); + co.zExpPayloadSize = efc_max_payload_size; + } + + co.sPacketFilterConfig.set(arg); + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + // This option is meaningless for the socket itself. + // It's set here just for the sake of setting it on a listener + // socket so that it is then applied on the group when a + // group connection is configuired. + const int val = cast_optval(optval, optlen); + + // Search if you already have SRTO_PEERIDLETIMEO set + + const int idletmo = co.iPeerIdleTimeout; + + // Both are in milliseconds. + // This option is RECORDED in microseconds, while + // idletmo is recorded in milliseconds, only translated to + // microseconds directly before use. + if (val >= idletmo) + { + LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val + << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.uStabilityTimeout = val * 1000; + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iRetransmitAlgo = cast_optval(optval, optlen); + } +}; + static struct SrtConfigSetter { setter_function* fn[SRTO_E_SIZE]; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 72b90195e..a9d2a0856 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -116,14 +116,6 @@ struct CSrtMuxerConfig struct CSrtConfig; -typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); - -template -struct CSrtConfigSetter -{ - static setter_function set; -}; - template class StringStorage { @@ -314,14 +306,6 @@ struct CSrtConfig: CSrtMuxerConfig } int set(SRT_SOCKOPT optName, const void* val, int size); - - // Could be later made a more robust version with - // dispatching to the right data type. - template - void set(const void* val, int size) - { - CSrtConfigSetter::set(*this, val, size); - } }; @@ -390,817 +374,4 @@ inline bool cast_optval(const void* optval, int optlen) return false; } -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int ival = cast_optval(optval, optlen); - if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iMSS = ival; - - // Packet size cannot be greater than UDP buffer size - if (co.iMSS > co.iUDPSndBufSize) - co.iMSS = co.iUDPSndBufSize; - if (co.iMSS > co.iUDPRcvBufSize) - co.iMSS = co.iUDPRcvBufSize; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - const int fc = cast_optval(optval, optlen); - if (fc < co.DEF_MIN_FLIGHT_PKT) - { - LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL); - } - - co.iFlightFlagSize = fc; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int bs = cast_optval(optval, optlen); - if (bs <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; - - if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) - co.iRcvBufSize = val / mssin_size; - else - co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; - - // recv buffer MUST not be greater than FC size - if (co.iRcvBufSize > co.iFlightFlagSize) - co.iRcvBufSize = co.iFlightFlagSize; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.Linger = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iUDPSndBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iUDPRcvBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bRendezvous = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndTimeOut = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvTimeOut = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bSynSending = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bSynRecving = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bReuseAddr = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.llMaxBW = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int val = cast_optval(optval, optlen); - if (!(val == -1) && !((val >= 1) && (val <= 255))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iIpTTL = cast_optval(optval); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iIpToS = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_BINDTODEVICE - using namespace std; - using namespace srt_logging; - - string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); - if (val.size() >= IFNAMSIZ) - { - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.sBindToDevice = val; -#else - (void)co; // prevent warning - (void)optval; - (void)optlen; - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.llInputBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.llMinInputBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int32_t val = cast_optval(optval, optlen); - if (val < 5 || val > 100) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iOverheadBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bDataSender = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bTSBPD = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvLatency = val; - co.iPeerLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iPeerLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bTLPktDrop = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndDropDelay = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_ENCRYPTION - // Password must be 10-80 characters. - // Or it can be empty to clear the password. - if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memset(&co.CryptoSecret, 0, sizeof(co.CryptoSecret)); - co.CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; - co.CryptoSecret.len = (optlen <= (int)sizeof(co.CryptoSecret.str) ? optlen : (int)sizeof(co.CryptoSecret.str)); - memcpy((co.CryptoSecret.str), optval, co.CryptoSecret.len); -#else - (void)co; // prevent warning - (void)optval; - if (optlen == 0) - return; // Allow to set empty passphrase if no encryption supported. - - LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_ENCRYPTION - const int v = cast_optval(optval, optlen); - int const allowed[4] = { - 0, // Default value, if this results for initiator, defaults to 16. See below. - 16, // AES-128 - 24, // AES-192 - 32 // AES-256 - }; - const int *const allowed_end = allowed + 4; - if (std::find(allowed, allowed_end, v) == allowed_end) - { - LOGC(aclog.Error, - log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - // Note: This works a little different in HSv4 and HSv5. - - // HSv4: - // The party that is set SRTO_SENDER will send KMREQ, and it will - // use default value 16, if SRTO_PBKEYLEN is the default value 0. - // The responder that receives KMRSP has nothing to say about - // PBKEYLEN anyway and it will take the length of the key from - // the initiator (sender) as a good deal. - // - // HSv5: - // The initiator (independently on the sender) will send KMREQ, - // and as it should be the sender to decide about the PBKEYLEN. - // Your application should do the following then: - // 1. The sender should set PBKEYLEN to the required value. - // 2. If the sender is initiator, it will create the key using - // its preset PBKEYLEN (or default 16, if not set) and the - // receiver-responder will take it as a good deal. - // 3. Leave the PBKEYLEN value on the receiver as default 0. - // 4. If sender is responder, it should then advertise the PBKEYLEN - // value in the initial handshake messages (URQ_INDUCTION if - // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case - // of rendezvous, as it is the matter of luck who of them will - // eventually become the initiator). This way the receiver - // being an initiator will set iSndCryptoKeyLen before setting - // up KMREQ for sending to the sender-responder. - // - // Note that in HSv5 if both sides set PBKEYLEN, the responder - // wins, unless the initiator is a sender (the effective PBKEYLEN - // will be the one advertised by the responder). If none sets, - // PBKEYLEN will default to 16. - - co.iSndCryptoKeyLen = v; -#else - (void)co; // prevent warning - (void)optval; - (void)optlen; - LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bRcvNakReport = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - using namespace srt::sync; - co.tdConnTimeOut = milliseconds_from(val); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bDriftTracer = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iMaxReorderTolerance = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.uSrtVersion = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.uMinimumPeerSrtVersion = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.sStreamName.set((const char*)optval, optlen); - } -}; - -template<> -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); - - // Translate alias - if (val == "vod") - val = "file"; - - bool res = SrtCongestion::exists(val); - if (!res) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.sCongestion.set(val); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bMessageAPI = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - const int val = cast_optval(optval, optlen); - if (val < 0) - { - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (val > SRT_LIVE_MAX_PLSIZE) - { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (!co.sPacketFilterConfig.empty()) - { - // This means that the filter might have been installed before, - // and the fix to the maximum payload size was already applied. - // This needs to be checked now. - srt::SrtFilterConfig fc; - if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) - { - // Break silently. This should not happen - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size - << " required for packet filter header"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - co.zExpPayloadSize = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - // XXX Note that here the configuration for SRTT_LIVE - // is the same as DEFAULT VALUES for these fields set - // in CUDT::CUDT. - switch (cast_optval(optval, optlen)) - { - case SRTT_LIVE: - // Default live options: - // - tsbpd: on - // - latency: 120ms - // - linger: off - // - congctl: live - // - extraction method: message (reading call extracts one message) - co.bTSBPD = true; - co.iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; - co.iPeerLatency = 0; - co.bTLPktDrop = true; - co.iSndDropDelay = 0; - co.bMessageAPI = true; - co.bRcvNakReport = true; - co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; - co.Linger.l_onoff = 0; - co.Linger.l_linger = 0; - co.sCongestion.set("live", 4); - break; - - case SRTT_FILE: - // File transfer mode: - // - tsbpd: off - // - latency: 0 - // - linger: on - // - congctl: file (original UDT congestion control) - // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) - co.bTSBPD = false; - co.iRcvLatency = 0; - co.iPeerLatency = 0; - co.bTLPktDrop = false; - co.iSndDropDelay = -1; - co.bMessageAPI = false; - co.bRcvNakReport = false; - co.zExpPayloadSize = 0; // use maximum - co.Linger.l_onoff = 1; - co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; - co.sCongestion.set("file", 4); - break; - - default: - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } -}; - -#if ENABLE_EXPERIMENTAL_BONDING -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iGroupConnect = cast_optval(optval, optlen); - } -}; -#endif - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - - const int val = cast_optval(optval, optlen); - if (val < 0) - { - LOGC(aclog.Error, - log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value - co.uKmRefreshRatePkt = (unsigned) val; - - if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) - return; // Both values are default - - const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; - const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - - if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) - { - co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; - LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" - << std::hex << co.uKmPreAnnouncePkt); - } - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - - const int val = cast_optval(optval, optlen); - if (val < 0) - { - LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; - const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - if (km_preanno > (kmref - 1) / 2) - { - LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) - << " - OPTION REJECTED."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.uKmPreAnnouncePkt = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bEnforcedEnc = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iPeerIdleTimeout = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iIpV6Only = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - std::string arg((const char*)optval, optlen); - // Parse the configuration string prematurely - srt::SrtFilterConfig fc; - srt::PacketFilter::Factory* fax = 0; - if (!srt::ParseFilterConfig(arg, (fc), (&fax))) - { - LOGC(aclog.Error, - log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " - "FILTERTYPE (" - << fc.type << ") must be installed (or builtin)"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - std::string error; - if (!fax->verifyConfig(fc, (error))) - { - LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect config: " << error); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.zExpPayloadSize > efc_max_payload_size) - { - LOGC(aclog.Warn, - log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " - << efc_max_payload_size << " bytes"); - co.zExpPayloadSize = efc_max_payload_size; - } - - co.sPacketFilterConfig.set(arg); - } -}; - -#if ENABLE_EXPERIMENTAL_BONDING -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - // This option is meaningless for the socket itself. - // It's set here just for the sake of setting it on a listener - // socket so that it is then applied on the group when a - // group connection is configuired. - const int val = cast_optval(optval, optlen); - - // Search if you already have SRTO_PEERIDLETIMEO set - - const int idletmo = co.iPeerIdleTimeout; - - // Both are in milliseconds. - // This option is RECORDED in microseconds, while - // idletmo is recorded in milliseconds, only translated to - // microseconds directly before use. - if (val >= idletmo) - { - LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val - << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.uStabilityTimeout = val * 1000; - } -}; -#endif - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iRetransmitAlgo = cast_optval(optval, optlen); - } -}; - -#if TEMPLATE -#endif - #endif From 127c85c1098a91864528235342735de54156de59 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 5 Jul 2021 19:30:54 +0800 Subject: [PATCH 215/790] [core] Put CSrtConfigSetter into anonymous namespace And use simple switch case. The library size is reduced from 1224 KB to 1216 KB on macOS build with: -DENABLE_TESTING=ON -DENABLE_UNITTESTS=ON -DENABLE_HEAVY_LOGGING=ON \ -DUSE_ENCLIB=mbedtls -DENABLE_EXPERIMENTAL_BONDING=ON -DENABLE_ENCRYPTION=OFF \ -DENABLE_STDCXX_SYNC=ON --- srtcore/socketconfig.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 8cb06a08f..2fcc475b2 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -54,6 +54,7 @@ written by extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); +namespace { typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); template @@ -872,15 +873,11 @@ struct CSrtConfigSetter } }; -static struct SrtConfigSetter +int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int optlen) { - setter_function* fn[SRTO_E_SIZE]; - - SrtConfigSetter() + switch (optName) { - memset(fn, 0, sizeof fn); - -#define DISPATCH(optname) fn[optname] = &CSrtConfigSetter::set; +#define DISPATCH(optname) case optname: CSrtConfigSetter::set(co, optval, optlen); return 0; DISPATCH(SRTO_MSS); DISPATCH(SRTO_FC); @@ -937,17 +934,16 @@ static struct SrtConfigSetter DISPATCH(SRTO_RETRANSMITALGO); #undef DISPATCH + default: + return -1; } -} srt_config_setter; +} + +} // anonymous namespace int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) { - setter_function* fn = srt_config_setter.fn[optName]; - if (!fn) - return -1; // No such option - - fn(*this, optval, optlen); // MAY THROW EXCEPTION. - return 0; + return dispatchSet(optName, *this, optval, optlen); } #if ENABLE_EXPERIMENTAL_BONDING From 877adfa5fc4c98050faaa5062c0ab5097d634ca1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Jul 2021 15:21:35 +0200 Subject: [PATCH 216/790] [docs] Removed unused SRTO_FC from config function in configuration guidelines. --- docs/API/configuration-guidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index 1e6d2111e..54d3cd15a 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -32,7 +32,7 @@ int getRbufSizePkts(int SRTO_RCVBUF, int SRTO_MSS, int SRTO_FC) // UDP header size is assumed to be 28 bytes // 20 bytes IPv4 + 8 bytes of UDP const int UDPHDR_SIZE = 28; - const in pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); + const int pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); return min(pkts, SRTO_FC); } @@ -88,7 +88,7 @@ where ```c++ -auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS, int SRTO_FC) +auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS) { const int UDPHDR_SIZE = 28; const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; @@ -99,7 +99,7 @@ auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int m // Configuring -const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS, SRTO_FC); +const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS); int optval = fc; int optlen = sizeof optval; From ea4edffc257bb634fe8944e530ed5fc355860c92 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Jul 2021 00:41:16 +0200 Subject: [PATCH 217/790] [core] Moved compiler attribute definitions --- srtcore/srt_attr_defs.h | 80 +++++++++++++++++++++++++++++++++++++---- srtcore/utilities.h | 72 +------------------------------------ 2 files changed, 75 insertions(+), 77 deletions(-) diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 24bc8bc84..82e6e5e3a 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -3,23 +3,91 @@ * Copyright (c) 2019 Haivision Systems Inc. * * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this + * License, v.2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ /***************************************************************************** The file contains various planform and compiler dependent attribute definitions used by SRT library internally. - -1. Attributes for thread safety analysis - - Clang (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). - - Other compilers: none. - *****************************************************************************/ #ifndef INC_SRT_ATTR_DEFS_H #define INC_SRT_ATTR_DEFS_H +// ATTRIBUTES: +// +// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) +// ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) +// ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode +// ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. +// ATR_CONSTEXPR: In C++11: `constexpr`. Otherwise empty. +// ATR_OVERRIDE: In C++11: `override`. Otherwise empty. +// ATR_FINAL: In C++11: `final`. Otherwise empty. + +#ifdef __GNUG__ +#define ATR_DEPRECATED __attribute__((deprecated)) +#else +#define ATR_DEPRECATED +#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, +// however it's only the "most required C++11 support". +#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only! +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#else +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_NOTHROW noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#endif +#elif defined(_MSC_VER) && _MSC_VER >= 1800 +// Microsoft Visual Studio supports C++11, but not fully, +// and still did not change the value of __cplusplus. Treat +// this special way. +// _MSC_VER == 1800 means Microsoft Visual Studio 2013. +#define HAVE_CXX11 1 +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_NOTHROW noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#else +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#endif +#else +#define HAVE_CXX11 0 +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#endif // __cplusplus + +#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1 +#error "The currently compiled application required C++11, but your compiler doesn't support it." +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Attributes for thread safety analysis +// - Clang TSA (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). +// - MSVC SAL (partially). +// - Other compilers: none. +/////////////////////////////////////////////////////////////////////////////// #if _MSC_VER >= 1920 // In case of MSVC these attributes have to preceed the attributed objects (variable, function). // E.g. SRT_ATTR_GUARDED_BY(mtx) int object; diff --git a/srtcore/utilities.h b/srtcore/utilities.h index ed96306f5..6d888b3d8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -16,81 +16,11 @@ written by #ifndef INC_SRT_UTILITIES_H #define INC_SRT_UTILITIES_H -// ATTRIBUTES: -// -// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) -// ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) -// ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode -// ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. -// ATR_CONSTEXPR: In C++11: `constexpr`. Otherwise empty. -// ATR_OVERRIDE: In C++11: `override`. Otherwise empty. -// ATR_FINAL: In C++11: `final`. Otherwise empty. - - -#ifdef __GNUG__ -#define ATR_DEPRECATED __attribute__((deprecated)) -#else -#define ATR_DEPRECATED -#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, -// however it's only the "most required C++11 support". -#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only! -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#else -#define HAVE_FULL_CXX11 1 -#define ATR_NOEXCEPT noexcept -#define ATR_NOTHROW noexcept -#define ATR_CONSTEXPR constexpr -#define ATR_OVERRIDE override -#define ATR_FINAL final -#endif - -// Microsoft Visual Studio supports C++11, but not fully, -// and still did not change the value of __cplusplus. Treat -// this special way. -// _MSC_VER == 1800 means Microsoft Visual Studio 2013. -#elif defined(_MSC_VER) && _MSC_VER >= 1800 -#define HAVE_CXX11 1 -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 -#define HAVE_FULL_CXX11 1 -#define ATR_NOEXCEPT noexcept -#define ATR_NOTHROW noexcept -#define ATR_CONSTEXPR constexpr -#define ATR_OVERRIDE override -#define ATR_FINAL final -#else -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#endif -#else -#define HAVE_CXX11 0 -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL - -#endif - -#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1 -#error "The currently compiled application required C++11, but your compiler doesn't support it." -#endif - - // Windows warning disabler #define _CRT_SECURE_NO_WARNINGS 1 #include "platform_sys.h" +#include "srt_attr_defs.h" // defines HAVE_CXX11 // Happens that these are defined, undefine them in advance #undef min From 158f35d6ba24d92d5a217f06ef402b133bfd50ae Mon Sep 17 00:00:00 2001 From: Laurent Bigonville Date: Fri, 30 Jul 2021 00:04:24 +0200 Subject: [PATCH 218/790] [core] Fix FTBFS on Debian kfreebsd Fixes: #2066 --- srtcore/epoll.cpp | 4 ++++ srtcore/utilities.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 5755c9dff..bfce672cc 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -58,6 +58,10 @@ modified by #include #include +#if defined(__FreeBSD_kernel__) +#include +#endif + #include "common.h" #include "epoll.h" #include "logging.h" diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 6d888b3d8..b1f21fbb2 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -115,7 +115,7 @@ written by # include -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) # include From 5ac27a3733c7938eab5fc147d10412b8304fafbd Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 2 Aug 2021 17:15:04 +0200 Subject: [PATCH 219/790] [tests] Tests for socket options API (#1929) --- docs/API/API-socket-options.md | 2 + test/any.hpp | 504 +++++++++++++++++++++++++++ test/filelist.maf | 2 + test/test_listen_callback.cpp | 6 +- test/test_socket_options.cpp | 598 ++++++++++++++++++++++++++++++++- 5 files changed, 1107 insertions(+), 5 deletions(-) create mode 100644 test/any.hpp diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 3c9f91556..b87d80ab0 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -847,6 +847,8 @@ dedicated network settings. MSS is not to be confused with the size of the UDP payload or SRT payload - this size is the size of the IP packet, including the UDP and SRT headers* +THe value of `SRTO_MSS` must not exceed `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. + [Return to list](#list-of-options) --- diff --git a/test/any.hpp b/test/any.hpp new file mode 100644 index 000000000..d47722bae --- /dev/null +++ b/test/any.hpp @@ -0,0 +1,504 @@ +// +// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. +// +// See also: +// + http://en.cppreference.com/w/cpp/any +// + http://en.cppreference.com/w/cpp/experimental/any +// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any +// + https://cplusplus.github.io/LWG/lwg-active.html#2509 +// +// +// Copyright (c) 2016 Denilson das MercĆŖs Amorim +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef LINB_ANY_HPP +#define LINB_ANY_HPP +#pragma once +#include +#include +#include + + +#if defined(PARTICLE) +#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS) +# define ANY_IMPL_NO_EXCEPTIONS +# endif +#else +// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, +// but you must ensure not to cast badly when passing an `any' object to any_cast(any) +#endif + +#if defined(PARTICLE) +#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI) +# define ANY_IMPL_NO_RTTI +# endif +#else +// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, +// in order to disable functions working with the typeid of a type +#endif + + +namespace linb +{ + +class bad_any_cast : public std::bad_cast +{ +public: + const char* what() const noexcept override + { + return "bad any cast"; + } +}; + +class any final +{ +public: + /// Constructs an object of type any with an empty state. + any() : + vtable(nullptr) + { + } + + /// Constructs an object of type any with an equivalent state as other. + any(const any& rhs) : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->copy(rhs.storage, this->storage); + } + } + + /// Constructs an object of type any with a state equivalent to the original state of other. + /// rhs is left in a valid but otherwise unspecified state. + any(any&& rhs) noexcept : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->move(rhs.storage, this->storage); + rhs.vtable = nullptr; + } + } + + /// Same effect as this->clear(). + ~any() + { + this->clear(); + } + + /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + this->construct(std::forward(value)); + } + + /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. + any& operator=(const any& rhs) + { + any(rhs).swap(*this); + return *this; + } + + /// Has the same effect as any(std::move(rhs)).swap(*this). + /// + /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid + /// but otherwise unspecified state. + any& operator=(any&& rhs) noexcept + { + any(std::move(rhs)).swap(*this); + return *this; + } + + /// Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any& operator=(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + any(std::forward(value)).swap(*this); + return *this; + } + + /// If not empty, destroys the contained object. + void clear() noexcept + { + if(!empty()) + { + this->vtable->destroy(storage); + this->vtable = nullptr; + } + } + + /// Returns true if *this has no contained object, otherwise false. + bool empty() const noexcept + { + return this->vtable == nullptr; + } + +#ifndef ANY_IMPL_NO_RTTI + /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). + const std::type_info& type() const noexcept + { + return empty()? typeid(void) : this->vtable->type(); + } +#endif + + /// Exchange the states of *this and rhs. + void swap(any& rhs) noexcept + { + if(this->vtable != rhs.vtable) + { + any tmp(std::move(rhs)); + + // move from *this to rhs. + rhs.vtable = this->vtable; + if(this->vtable != nullptr) + { + this->vtable->move(this->storage, rhs.storage); + //this->vtable = nullptr; -- unneeded, see below + } + + // move from tmp (previously rhs) to *this. + this->vtable = tmp.vtable; + if(tmp.vtable != nullptr) + { + tmp.vtable->move(tmp.storage, this->storage); + tmp.vtable = nullptr; + } + } + else // same types + { + if(this->vtable != nullptr) + this->vtable->swap(this->storage, rhs.storage); + } + } + +private: // Storage and Virtual Method Table + union storage_union + { + using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; + + void* dynamic; + stack_storage_t stack; // 2 words for e.g. shared_ptr + }; + + /// Base VTable specification. + struct vtable_type + { + // Note: The caller is responssible for doing .vtable = nullptr after destructful operations + // such as destroy() and/or move(). + +#ifndef ANY_IMPL_NO_RTTI + /// The type of the object this vtable is for. + const std::type_info& (*type)() noexcept; +#endif + + /// Destroys the object in the union. + /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. + void(*destroy)(storage_union&) noexcept; + + /// Copies the **inner** content of the src union into the yet unitialized dest union. + /// As such, both inner objects will have the same state, but on separate memory locations. + void(*copy)(const storage_union& src, storage_union& dest); + + /// Moves the storage from src to the yet unitialized dest union. + /// The state of src after this call is unspecified, caller must ensure not to use src anymore. + void(*move)(storage_union& src, storage_union& dest) noexcept; + + /// Exchanges the storage between lhs and rhs. + void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; + }; + + /// VTable for dynamically allocated storage. + template + struct vtable_dynamic + { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info& type() noexcept + { + return typeid(T); + } +#endif + + static void destroy(storage_union& storage) noexcept + { + //assert(reinterpret_cast(storage.dynamic)); + delete reinterpret_cast(storage.dynamic); + } + + static void copy(const storage_union& src, storage_union& dest) + { + dest.dynamic = new T(*reinterpret_cast(src.dynamic)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + dest.dynamic = src.dynamic; + src.dynamic = nullptr; + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + // just exchage the storage pointers. + std::swap(lhs.dynamic, rhs.dynamic); + } + }; + + /// VTable for stack allocated storage. + template + struct vtable_stack + { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info& type() noexcept + { + return typeid(T); + } +#endif + + static void destroy(storage_union& storage) noexcept + { + reinterpret_cast(&storage.stack)->~T(); + } + + static void copy(const storage_union& src, storage_union& dest) + { + new (&dest.stack) T(reinterpret_cast(src.stack)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + // one of the conditions for using vtable_stack is a nothrow move constructor, + // so this move constructor will never throw a exception. + new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); + destroy(src); + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + storage_union tmp_storage; + move(rhs, tmp_storage); + move(lhs, rhs); + move(tmp_storage, lhs); + } + }; + + /// Whether the type T must be dynamically allocated or can be stored on the stack. + template + struct requires_allocation : + std::integral_constant::value // N4562 Ā§6.3/3 [any.class] + && sizeof(T) <= sizeof(storage_union::stack) + && std::alignment_of::value <= std::alignment_of::value)> + {}; + + /// Returns the pointer to the vtable of the type T. + template + static vtable_type* vtable_for_type() + { + using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; + static vtable_type table = { +#ifndef ANY_IMPL_NO_RTTI + VTableType::type, +#endif + VTableType::destroy, + VTableType::copy, VTableType::move, + VTableType::swap, + }; + return &table; + } + +protected: + template + friend const T* any_cast(const any* operand) noexcept; + template + friend T* any_cast(any* operand) noexcept; + +#ifndef ANY_IMPL_NO_RTTI + /// Same effect as is_same(this->type(), t); + bool is_typed(const std::type_info& t) const + { + return is_same(this->type(), t); + } +#endif + +#ifndef ANY_IMPL_NO_RTTI + /// Checks if two type infos are the same. + /// + /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the + /// type infos, otherwise does an actual comparision. Checking addresses is + /// only a valid approach when there's no interaction with outside sources + /// (other shared libraries and such). + static bool is_same(const std::type_info& a, const std::type_info& b) + { +#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE + return &a == &b; +#else + return a == b; +#endif + } +#endif + + /// Casts (with no type_info checks) the storage pointer as const T*. + template + const T* cast() const noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + + /// Casts (with no type_info checks) the storage pointer as T*. + template + T* cast() noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + +private: + storage_union storage; // on offset(0) so no padding for align + vtable_type* vtable; + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + storage.dynamic = new T(std::forward(value)); + } + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + new (&storage.stack) T(std::forward(value)); + } + + /// Chooses between stack and dynamic allocation for the type decay_t, + /// assigns the correct vtable, and constructs the object on our storage. + template + void construct(ValueType&& value) + { + using T = typename std::decay::type; + + this->vtable = vtable_for_type(); + + do_construct(std::forward(value)); + } +}; + + + +namespace detail +{ + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) + { + return std::move(*p); + } + + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) + { + return *p; + } +} + +/// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(const any& operand) +{ + auto p = any_cast::type>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return *p; +} + +/// Performs *any_cast>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(any& operand) +{ + auto p = any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return *p; +} + +/// +/// If ValueType is MoveConstructible and isn't a lvalue reference, performs +/// std::move(*any_cast>(&operand)), otherwise +/// *any_cast>(&operand). Throws bad_any_cast on failure. +/// +template +inline ValueType any_cast(any&& operand) +{ + using can_move = std::integral_constant::value + && !std::is_lvalue_reference::value>; + + auto p = any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return detail::any_cast_move_if_true(p, can_move()); +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline const ValueType* any_cast(const any* operand) noexcept +{ + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline ValueType* any_cast(any* operand) noexcept +{ + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; +} + +} + +namespace std +{ + inline void swap(linb::any& lhs, linb::any& rhs) noexcept + { + lhs.swap(rhs); + } +} + +#endif diff --git a/test/filelist.maf b/test/filelist.maf index d92cabd9d..0cb25cee8 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -1,3 +1,5 @@ +HEADERS +any.hpp SOURCES test_buffer.cpp diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index c8a441273..7abe6916a 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -183,8 +183,12 @@ int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion #if SRT_ENABLE_ENCRYPTION cerr << "TEST: Setting password '" << exp_pw << "' as per user '" << username << "'\n"; - srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()); + EXPECT_EQ(srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()), SRT_SUCCESS); #endif + + // Checking that SRTO_RCVLATENCY (PRE option) can be altered in the listener callback. + int optval = 200; + EXPECT_EQ(srt_setsockflag(ns, SRTO_RCVLATENCY, &optval, sizeof optval), SRT_SUCCESS); return 0; } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index d2e1da704..47700c9a6 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -13,9 +13,11 @@ #include #include #include +#include // SRT includes -#include +#include "any.hpp" +#include "socketconfig.h" #include "srt.h" using namespace std; @@ -36,15 +38,26 @@ class TestSocketOptions } public: - void StartListener() + void BindListener() { // Specify address of the listener sockaddr* psa = (sockaddr*)&m_sa; ASSERT_NE(srt_bind(m_listen_sock, psa, sizeof m_sa), SRT_ERROR); + } + + void StartListener() + { + BindListener(); srt_listen(m_listen_sock, 1); } + int Connect() + { + sockaddr* psa = (sockaddr*)&m_sa; + return srt_connect(m_caller_sock, psa, sizeof m_sa); + } + SRTSOCKET EstablishConnection() { auto accept_async = [](SRTSOCKET listen_sock) { @@ -55,8 +68,7 @@ class TestSocketOptions }; auto accept_res = async(launch::async, accept_async, m_listen_sock); - sockaddr* psa = (sockaddr*)&m_sa; - const int connect_res = srt_connect(m_caller_sock, psa, sizeof m_sa); + const int connect_res = Connect(); EXPECT_EQ(connect_res, SRT_SUCCESS); const SRTSOCKET accepted_sock = accept_res.get(); @@ -108,6 +120,584 @@ class TestSocketOptions }; +enum class RestrictionType +{ + PREBIND = 0, + PRE = 1, + POST = 2 +}; + +const char* RestrictionTypeStr(RestrictionType val) +{ + switch (val) + { + case RestrictionType::PREBIND: + return "PREBIND"; + break; + case RestrictionType::PRE: + return "PRE"; + break; + case RestrictionType::POST: + return "POST"; + break; + default: + break; + } + return "INVALID"; +} + +struct OptionTestEntry +{ + SRT_SOCKOPT optid; + const char* optname; // TODO: move to a separate array, function or std::map. + RestrictionType restriction; // TODO: consider using SRTO_R_PREBIND, etc. from core.cpp + size_t opt_len; + linb::any min_val; + linb::any max_val; + linb::any dflt_val; + linb::any ndflt_val; + vector invalid_vals; +}; + +static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. +static const size_t DFT_MTU_SIZE = 1500; // Default MTU size +static const size_t SRT_PKT_SIZE = DFT_MTU_SIZE - UDP_HDR_SIZE; // MTU without UDP header + +const OptionTestEntry g_test_matrix_options[] = +{ + // Option ID, Option Name | Restriction | optlen | min | max | default | nondefault | invalid vals | + //SRTO_BINDTODEVICE + //{ SRTO_CONGESTION, "SRTO_CONGESTION", RestrictionType::PRE, 4, "live", "file", "live", "file", {"liv", ""} }, + { SRTO_CONNTIMEO, "SRTO_CONNTIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 3000, 250, {-1} }, + { SRTO_DRIFTTRACER, "SRTO_DRIFTTRACER", RestrictionType::POST, sizeof(bool), false, true, true, false, {} }, + { SRTO_ENFORCEDENCRYPTION, "SRTO_ENFORCEDENCRYPTION", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + //SRTO_EVENT + { SRTO_FC, "SRTO_FC", RestrictionType::PRE, sizeof(int), 32, INT32_MAX, 25600, 10000, {-1, 31} }, + //SRTO_GROUPCONNECT + //SRTO_GROUPSTABTIMEO + //SRTO_GROUPTYPE + //SRTO_INPUTBW + //SRTO_IPTOS + //SRTO_IPTTL + //SRTO_IPV6ONLY + //SRTO_ISN + { SRTO_KMPREANNOUNCE, "SRTO_KMPREANNOUNCE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} }, + { SRTO_KMREFRESHRATE, "SRTO_KMREFRESHRATE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} }, + //SRTO_KMSTATE + { 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_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, {} }, + { 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, {} }, + { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101} }, + //SRTO_PACKETFILTER + //SRTO_PASSPHRASE + { SRTO_PAYLOADSIZE, "SRTO_PAYLOADSIZE", RestrictionType::PRE, sizeof(int), 0, 1456, 1316, 1400, {-1, 1500} }, + //SRTO_PBKEYLEN + //SRTO_PEERIDLETIMEO + { SRTO_PEERIDLETIMEO, "SRTO_PEERIDLETIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 5000, 4500, {-1} }, + { SRTO_PEERLATENCY, "SRTO_PEERLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 180, {-1} }, + //SRTO_PEERVERSION + { SRTO_RCVBUF, "SRTO_RCVBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, + //SRTO_RCVDATA + //SRTO_RCVKMSTATE + { SRTO_RCVLATENCY, "SRTO_RCVLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 1100, {-1} }, + //SRTO_RCVSYN + { SRTO_RCVTIMEO, "SRTO_RCVTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 2000, {-2} }, + //SRTO_RENDEZVOUS + //SRTO_RETRANSMITALGO + //SRTO_REUSEADDR + //SRTO_SENDER + { SRTO_SNDBUF, "SRTO_SNDBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, + //SRTO_SNDDATA + { SRTO_SNDDROPDELAY, "SRTO_SNDDROPDELAY", RestrictionType::POST, sizeof(int), -1, INT32_MAX, 0, 1500, {-2} }, + //SRTO_SNDKMSTATE + //SRTO_SNDSYN + { SRTO_SNDTIMEO, "SRTO_SNDTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 1400, {-2} }, + //SRTO_STATE + //SRTO_STREAMID + { SRTO_TLPKTDROP, "SRTO_TLPKTDROP", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + //SRTO_TRANSTYPE + //SRTO_TSBPDMODE + //SRTO_UDP_RCVBUF + //SRTO_UDP_SNDBUF + //SRTO_VERSION +}; + + +template +void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) +{ + ValueType opt_val; + int opt_len = 0; + EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) + << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); + + EXPECT_EQ(opt_val, value) << desc << ": Wrong " << entry.optname << " value " << opt_val; + EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; +} + +typedef char const* strptr; +template<> +void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const strptr& value, const char* desc) +{ + char opt_val[16]; + int opt_len = 0; + EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) + << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); + + EXPECT_EQ(strncmp(opt_val, value, min(opt_len, entry.opt_len)), 0) << desc << ": Wrong " << entry.optname << " value " << opt_val; + EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; +} + +template +void CheckSetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, int expect_return, const char* desc) +{ + ValueType opt_val = value; + int opt_len = entry.opt_len; + EXPECT_EQ(srt_setsockopt(sock, 0, entry.optid, &opt_val, opt_len), expect_return) + << "Setting " << entry.optname << " to " << opt_val << " must " << (expect_return == SRT_SUCCESS ? "succeed" : "fail"); + + if (expect_return == SRT_SUCCESS) + { + CheckGetSockOpt(entry, sock, value, desc); + } + // TODO: else check the previous value is in force +} + +template +bool CheckDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType dflt_val = linb::any_cast(entry.dflt_val); + CheckGetSockOpt(entry, sock, dflt_val, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " default value type: " << entry.dflt_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckSetNonDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, int expected_return, const char* desc) +{ + try { + /*const ValueType dflt_val = linb::any_cast(entry.dflt_val); + const ValueType min_val = linb::any_cast(entry.min_val); + const ValueType max_val = linb::any_cast(entry.max_val);*/ + //const ValueType ndflt_val = (min_val != dflt_val) ? min_val : max_val; + + const ValueType ndflt_val = linb::any_cast(entry.ndflt_val);; + + CheckSetSockOpt(entry, sock, ndflt_val, expected_return, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " non-default value type: " << entry.ndflt_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckMinValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType min_val = linb::any_cast(entry.min_val); + CheckSetSockOpt(entry, sock, min_val, SRT_SUCCESS, desc); + + const ValueType dflt_val = linb::any_cast(entry.dflt_val); + CheckSetSockOpt(entry, sock, dflt_val, SRT_SUCCESS, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " min value type: " << entry.min_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckMaxValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType max_val = linb::any_cast(entry.max_val); + CheckSetSockOpt(entry, sock, max_val, SRT_SUCCESS, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " max value type: " << entry.max_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char* sock_name) +{ + for (const auto inval : entry.invalid_vals) + { + try { + const ValueType val = linb::any_cast(inval); + CheckSetSockOpt(entry, sock, val, SRT_ERROR, "[Caller, invalid val]"); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " value type: " << inval.type().name() << "\n"; + return false; + } + } + + return true; +} + +TEST_F(TestSocketOptions, DefaultVals) +{ + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, default]"; + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(const char*)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << entry.optname << ": Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +TEST_F(TestSocketOptions, MaxVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + if (entry.optid == SRTO_KMPREANNOUNCE || entry.optid == SRTO_KMREFRESHRATE) + { + cerr << "Skipping " << entry.optname << "\n"; + continue; + } + + const char* test_desc = "[Caller, max value]"; + if (entry.max_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else if (entry.max_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else if (entry.max_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << "Unexpected type " << entry.max_val.type().name(); + } + + // TODO: back to default ? + } +} + +TEST_F(TestSocketOptions, MinVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, min val]"; + if (entry.min_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else if (entry.min_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else if (entry.min_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << entry.optname << ": Unexpected type " << entry.min_val.type().name(); + } + + // TODO: back to default + } +} + +TEST_F(TestSocketOptions, InvalidVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + + // TODO: expect default is still in force? + } +} + + + +// TODO: taken from test_enforced_encryption +static const char* const socket_state_array[] = { + "IGNORE_SRTS", + "SRTS_INVALID", + "SRTS_INIT", + "SRTS_OPENED", + "SRTS_LISTENING", + "SRTS_CONNECTING", + "SRTS_CONNECTED", + "SRTS_BROKEN", + "SRTS_CLOSING", + "SRTS_CLOSED", + "SRTS_NONEXIST" +}; + +// A trick that allows the array to be indexed by -1 +const char* const* g_socket_state = socket_state_array + 1; + +#if 0 +// No socket option can be set in blocking mode because m_ConnectionLock is required by both srt_setsockopt and srt_connect +// TODO: Use non-blocking mode +TEST_F(TestSocketOptions, RestrictionCallerConnecting) +{ + // The default SRTO_CONNTIMEO is 3 seconds. It is assumed all socket options could be checked. + auto connect_async = [this]() { + return Connect(); + }; + auto connect_res = async(launch::async, connect_async); + + for (int i = 0; i < 100; ++i) + { + if (srt_getsockstate(m_caller_sock) == SRTS_CONNECTING) + break; + + this_thread::sleep_for(chrono::microseconds(100)); + } + + cout << "Running test\n"; + + for (const auto& entry : g_test_matrix_options) + { + if (entry.restriction != RestrictionType::PRE) + continue; + + // Setting a valid minimum value + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, entry.optid, &entry.min_val, entry.opt_len), SRT_ERROR) + << "Setting " << entry.optname << " (PRE) must not succeed while connecting. Sock state: " << g_socket_state[srt_getsockstate(m_caller_sock)]; + } + + connect_res.get(); +} +#endif + +TEST_F(TestSocketOptions, RestrictionBind) +{ + BindListener(); + + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, after bind]"; + const int expected_res = (entry.restriction == RestrictionType::PREBIND) ? SRT_ERROR : SRT_SUCCESS; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +// Check that only socket option with POST binding can be set on a listener socket in "listening" state. +TEST_F(TestSocketOptions, RestrictionListening) +{ + StartListener(); + + for (const auto& entry : g_test_matrix_options) + { + const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS; + + // Setting a valid minimum value + const char* test_desc ="[Listener, listening]"; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +// Check that only socket option with POST binding can be set on a connected socket (caller and accepted). +TEST_F(TestSocketOptions, RestrictionConnected) +{ + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + for (const auto& entry : g_test_matrix_options) + { + const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS; + + // Setting a valid minimum value + for (SRTSOCKET sock : { m_caller_sock, accepted_sock }) + { + const char* test_desc = sock == m_caller_sock ? "[Caller, connected]" : "[Accepted, connected]"; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } + } +} + +// TODO: TEST_F(TestSocketOptions, CheckInheritedAfterConnection) +// Check that accepted socket has correct socket option values. +// Check setting and getting SRT_MININPUTBW +TEST_F(TestSocketOptions, TLPktDropInherits) +{ + const bool tlpktdrop_dflt = true; + const bool tlpktdrop_new = false; + + bool optval = tlpktdrop_dflt; + int optlen = (int)(sizeof optval); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &tlpktdrop_new, sizeof tlpktdrop_new), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, tlpktdrop_new); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (SRTSOCKET sock : { m_listen_sock, accepted_sock }) + { + optval = tlpktdrop_dflt; + optlen = (int)(sizeof optval); + EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optlen, (int)(sizeof optval)); + EXPECT_EQ(optval, tlpktdrop_new); + } + + this_thread::sleep_for(chrono::seconds(2)); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +TEST_F(TestSocketOptions, Latency) +{ + const int latency_a = 140; + const int latency_b = 100; + const int latency_dflt = 120; + + int optval; + int optlen = (int)(sizeof optval); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &latency_a, sizeof latency_a), SRT_SUCCESS); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &latency_b, sizeof latency_b), SRT_SUCCESS); + + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_b); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check caller socket + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_dflt); + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_dflt); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + /// A regression test for issue #735, fixed by PR #843. /// Checks propagation of listener's socket option SRTO_LOSSMAXTTL /// on SRT sockets being accepted. From 8f169a9445a52f27f074cbc7b5f2bf92dd24e8e2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 3 Aug 2021 12:19:13 +0200 Subject: [PATCH 220/790] [core] Changed the default SRTO_RETRANSMITALGO to 1 (#2069) SRTO_TRANSTYPE defaults are updated accordingly. --- docs/API/API-socket-options.md | 8 +++---- docs/API/API.md | 38 ++++++++++++++++++++-------------- srtcore/socketconfig.cpp | 8 ++++++- srtcore/socketconfig.h | 2 +- test/test_socket_options.cpp | 2 +- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index b87d80ab0..84606135a 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -239,7 +239,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | | [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | | [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | | [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | | [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | @@ -1275,12 +1275,12 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | -| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | Retransmission algorithm to use (SENDER option): -- 0 - Default (retransmit on every loss report). -- 1 - Reduced retransmissions (not more often than once per RTT); reduced +- 0 - Retransmit on every loss report (higher overhead, but slightly higher chance to recover a lost packet). +- 1 - Reduced retransmissions (retransmit not more often than once per RTT); reduced bandwidth consumption. This option is effective only on the sending side. It influences the decision diff --git a/docs/API/API.md b/docs/API/API.md index 594f9a12d..faca780c7 100644 --- a/docs/API/API.md +++ b/docs/API/API.md @@ -521,14 +521,15 @@ either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [socket options](API-socket-options.md): -- `SRTO_TSBPDMODE` = true -- `SRTO_RCVLATENCY` = 120 -- `SRTO_PEERLATENCY` = 0 -- `SRTO_TLPKTDROP` = true -- `SRTO_MESSAGEAPI` = true -- `SRTO_NAKREPORT` = true -- `SRTO_PAYLOADSIZE` = 1316 -- `SRTO_CONGESTION` = "live" +- [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = true +- [`SRTO_RCVLATENCY`](API-socket-options.md#SRTO_RCVLATENCY) = 120 +- [`SRTO_PEERLATENCY`](API-socket-options.md#SRTO_PEERLATENCY) = 0 +- [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) = true +- [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = true +- [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT) = true +- [`SRTO_RETRANSMITALGO`](API-socket-options.md#SRTO_RETRANSMITALGO) = 1 +- [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) = 1316 +- [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION) = "live" In this mode, every call to a sending function is allowed to send only so much data, as declared by `SRTO_PAYLOADSIZE`, whose value is still @@ -580,6 +581,10 @@ loss report itself was lost. Without it, the loss report will be always reported just once and never repeated again, and then the lost payload packet will be probably dropped by the TLPKTDROP mechanism. +- `SRTO_RETRANSMITALGO`: Given the receiver sends periodic NAK reports, +the sender can reduce the retransmission overhead by not retransmitting a loss +more often than once per RTT (value 1). + - `SRTO_PAYLOADSIZE`: Default value is for MPEG TS. If you are going to use SRT to send any different kind of payload, such as, for example, wrapping a live stream in very small frames, then you can use a bigger @@ -598,14 +603,15 @@ NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [socket options](API-socket-options.md): -- `SRTO_TSBPDMODE` = false -- `SRTO_RCVLATENCY` = 0 -- `SRTO_PEERLATENCY` = 0 -- `SRTO_TLPKTDROP` = false -- `SRTO_MESSAGEAPI` = false -- `SRTO_NAKREPORT` = false -- `SRTO_PAYLOADSIZE` = 0 -- `SRTO_CONGESTION` = "file" +- [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = false +- [`SRTO_RCVLATENCY`](API-socket-options.md#SRTO_RCVLATENCY) = 0 +- [`SRTO_PEERLATENCY`](API-socket-options.md#SRTO_PEERLATENCY) = 0 +- [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) = false +- [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = false +- [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT) = false +- [`SRTO_RETRANSMITALGO`](API-socket-options.md#SRTO_RETRANSMITALGO) = 0 +- [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) = 0 +- [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION) = "file" In this mode, calling a sending function is allowed to potentially send virtually any size of data. The sending function will HANGUP only if the diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 2fcc475b2..a62881cd7 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -656,6 +656,7 @@ struct CSrtConfigSetter co.iSndDropDelay = 0; co.bMessageAPI = true; co.bRcvNakReport = true; + co.iRetransmitAlgo = 1; co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; co.Linger.l_onoff = 0; co.Linger.l_linger = 0; @@ -676,6 +677,7 @@ struct CSrtConfigSetter co.iSndDropDelay = -1; co.bMessageAPI = false; co.bRcvNakReport = false; + co.iRetransmitAlgo = 0; co.zExpPayloadSize = 0; // use maximum co.Linger.l_onoff = 1; co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; @@ -869,7 +871,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRetransmitAlgo = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0 || val > 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRetransmitAlgo = val; } }; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index a9d2a0856..3e41077d3 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -271,7 +271,7 @@ struct CSrtConfig: CSrtMuxerConfig , iGroupConnect(0) , iPeerIdleTimeout(COMM_RESPONSE_TIMEOUT_MS) , uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) - , iRetransmitAlgo(0) + , iRetransmitAlgo(1) , llInputBW(0) , llMinInputBW(0) , iOverheadBW(25) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 47700c9a6..bf37513ce 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -209,7 +209,7 @@ const OptionTestEntry g_test_matrix_options[] = //SRTO_RCVSYN { SRTO_RCVTIMEO, "SRTO_RCVTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 2000, {-2} }, //SRTO_RENDEZVOUS - //SRTO_RETRANSMITALGO + { SRTO_RETRANSMITALGO, "SRTO_RETRANSMITALGO", RestrictionType::PRE, sizeof(int), 0, 1, 1, 0, {-1, 2} }, //SRTO_REUSEADDR //SRTO_SENDER { SRTO_SNDBUF, "SRTO_SNDBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, From 1a85c029af0c89c7d37f9cf71731bd551db3ec5c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 3 Aug 2021 12:39:58 +0200 Subject: [PATCH 221/790] [core] Annotating CUDT::m_pCryptoControl locking behavior (#2070) --- srtcore/core.cpp | 11 ++++------- srtcore/core.h | 22 ++++++++++++++++------ srtcore/sync.h | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 75c0171ae..cf01d9e35 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1497,14 +1497,11 @@ bool srt::CUDT::createSrtHandshake( w_hs.m_iType |= CHandShake::HS_EXT_HSREQ; bool have_sid = false; - if (srths_cmd == SRT_CMD_HSREQ) + if (srths_cmd == SRT_CMD_HSREQ && !m_config.sStreamName.empty()) { - if (!m_config.sStreamName.empty()) - { - have_sid = true; - w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; - logext << ",SID"; - } + have_sid = true; + w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; + logext << ",SID"; } // If this is a response, we have also information diff --git a/srtcore/core.h b/srtcore/core.h index d190780a0..9a1ad01f0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -302,6 +302,8 @@ class CUDT static std::vector existingSockets(); void addressAndSend(CPacket& pkt); + + SRT_ATTR_REQUIRES(m_ConnectionLock) void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); bool isOPT_TsbPd() const { return m_config.bTSBPD; } @@ -457,7 +459,8 @@ class CUDT /// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE) /// @retval -1 Connection failed - SRT_ATR_NODISCARD EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT; // This function works in case of HSv5 rendezvous. It changes the state // according to the present state and received message type, as well as the @@ -476,9 +479,15 @@ class CUDT /// @param response incoming handshake response packet to be interpreted /// @param serv_addr incoming packet's address /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) - SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); - SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); - SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); + + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr); @@ -708,7 +717,8 @@ class CUDT int m_iTsbPdDelay_ms; // Rx delay to absorb burst, in milliseconds int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst, in milliseconds bool m_bTLPktDrop; // Enable Too-late Packet Drop - UniquePtr m_pCryptoControl; // Congestion control SRT class (small data extension) + SRT_ATTR_PT_GUARDED_BY(m_ConnectionLock) + UniquePtr m_pCryptoControl; // Crypto control module CCache* m_pCache; // Network information cache // Congestion control @@ -932,7 +942,7 @@ class CUDT // Failure to create the crypter means that an encrypted // connection should be rejected if ENFORCEDENCRYPTION is on. - SRT_ATR_NODISCARD + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool createCrypter(HandshakeSide side, bool bidi); private: // Generation and processing of packets diff --git a/srtcore/sync.h b/srtcore/sync.h index 081cb41e4..d78aed980 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -446,7 +446,7 @@ class SRT_ATTR_SCOPED_CAPABILITY UniqueLock inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); } -inline bool tryEnterCS(Mutex& m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } +inline bool tryEnterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); } From 73cad8dab15035d5f320a7e8869d7986649cbc8e Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 29 Mar 2021 20:07:48 +0800 Subject: [PATCH 222/790] [core] Refactor ThreadName implementation * Add support for macOS and iOS * Add test_threadname --- srtcore/threadname.h | 182 ++++++++++++++++++++++++++++----------- test/filelist.maf | 1 + test/test_threadname.cpp | 61 +++++++++++++ 3 files changed, 192 insertions(+), 52 deletions(-) create mode 100644 test/test_threadname.cpp diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 38fea471b..6fd901aff 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -16,85 +16,163 @@ written by #ifndef INC_SRT_THREADNAME_H #define INC_SRT_THREADNAME_H -#ifdef __linux__ - +#if defined(__APPLE__) || defined(__linux__) +#if defined(__linux__) #include +#endif + +#include +#endif + #include +#include +#include + +#include "sync.h" class ThreadName { - char old_name[128]; - char new_name[128]; - bool good; -public: - static const size_t BUFSIZE = 128; +#if defined(__APPLE__) || defined(__linux__) - static bool get(char* namebuf) + class ThreadNameImpl { - return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; - } + public: + static const size_t BUFSIZE = 64; + static const bool DUMMY_IMPL = false; - static bool set(const char* name) - { - return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; - } + static bool get(char* namebuf) + { +#if defined(__linux__) + // since Linux 2.6.11. The buffer should allow space for up to 16 + // bytes; the returned string will be null-terminated. + return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; +#elif defined(__APPLE__) + // since macos(10.6), ios(3.2) + return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0; +#else +#error "unsupported platform" +#endif + } + static bool set(const char* name) + { +#if defined(__linux__) + // The name can be up to 16 bytes long, including the terminating + // null byte. (If the length of the string, including the terminating + // null byte, exceeds 16 bytes, the string is silently truncated.) + return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; +#elif defined(__APPLE__) + // since macos(10.6), ios(3.2) + return pthread_setname_np(name) == 0; +#else +#error "unsupported platform" +#endif + } - ThreadName(const char* name) - { - if ( (good = get(old_name)) ) + ThreadNameImpl(const char* name) { - snprintf(new_name, 127, "%s", name); - new_name[127] = 0; - prctl(PR_SET_NAME, (unsigned long)new_name, 0, 0); + reset = false; + tid = pthread_self(); + + if (!get(old_name)) + return; + + reset = set(name); + if (reset) + return; + + // Try with a shorter name. 15 is the upper limit supported by Linux, + // other platforms should support a larger value. So 15 should works + // on all platforms. + size_t max_len = 15; + if (std::strlen(name) > max_len) + reset = set(std::string(name, max_len).c_str()); } - } - ~ThreadName() - { - if ( good ) - prctl(PR_SET_NAME, (unsigned long)old_name, 0, 0); - } -}; + ~ThreadNameImpl() + { + if (!reset) + return; + + // ensure it's called on the right thread + if (tid == pthread_self()) + set(old_name); + } + + private: + bool reset; + pthread_t tid; + char old_name[BUFSIZE]; + }; #else -#include "sync.h" + class ThreadNameImpl + { + public: + static const bool DUMMY_IMPL = true; + static const size_t BUFSIZE = 64; -// Fallback version, which simply reports the thread name as -// T, and custom names used with `set` are ignored. -// If you know how to implement this for other systems than -// Linux, you can make another conditional. This one is now -// the "ultimate fallback". + static bool get(char* output) + { + // The default implementation will simply try to get the thread ID + std::ostringstream bs; + bs << "T" << srt::sync::this_thread::get_id(); + size_t s = bs.str().copy(output, BUFSIZE - 1); + output[s] = '\0'; + return true; + } + + static bool set(const char*) { return false; } + + ThreadNameImpl(const char*) {} + + ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version + { + } + }; + +#endif // platform dependent impl + + // Why delegate to impl: + // 1. to make sure implementation on different platforms have the same interface. + // 2. it's simple to add some wrappers like get(const std::string &). + ThreadNameImpl impl; -class ThreadName -{ public: - static const size_t BUFSIZE = 128; + static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL; + static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE; - static bool get(char* output) - { - // The default implementation will simply try to get the thread ID - std::ostringstream bs; - bs << "T" << srt::sync::this_thread::get_id(); - size_t s = bs.str().copy(output, BUFSIZE-1); - output[s] = '\0'; - return true; + // len should >= BUFSIZE + static bool get(char* output) { + return ThreadNameImpl::get(output); } - static bool set(const char*) { return false; } - ThreadName(const char*) + static bool get(std::string& name) { + char buf[BUFSIZE]; + bool ret = get(buf); + if (ret) + name = buf; + return ret; } - ~ThreadName() // just to make it "non-trivially-destructible" for compatibility with normal version + // note: set can fail if name is too long. The upper limit is platform + // dependent. strlen(name) <= 15 should work on most of the platform. + static bool set(const char* name) { return ThreadNameImpl::set(name); } + + static bool set(const std::string& name) { return set(name.c_str()); } + + ThreadName(const char* name) + : impl(name) { } + ThreadName(const std::string& name) + : impl(name.c_str()) + { + } }; - - -#endif #endif diff --git a/test/filelist.maf b/test/filelist.maf index 0cb25cee8..f39dfe2e6 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -19,6 +19,7 @@ test_muxer.cpp test_seqno.cpp test_socket_options.cpp test_sync.cpp +test_threadname.cpp test_timer.cpp test_unitqueue.cpp test_utilities.cpp diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp new file mode 100644 index 000000000..f9b1d1d8c --- /dev/null +++ b/test/test_threadname.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include "gtest/gtest.h" +#include "threadname.h" + +TEST(ThreadName, GetSet) +{ + std::string name("getset"); + char buf[ThreadName::BUFSIZE * 2]; + + memset(buf, 'a', sizeof(buf)); + ASSERT_EQ(ThreadName::get(buf), true); + // ensure doesn't write out-of-range + size_t max = ThreadName::BUFSIZE - 1; + ASSERT_LE(strlen(buf), max); + + if (ThreadName::DUMMY_IMPL) + return; + + ASSERT_EQ(ThreadName::set(name), true); + memset(buf, 'a', sizeof(buf)); + ASSERT_EQ(ThreadName::get(buf), true); + ASSERT_EQ(buf, name); +} + +TEST(ThreadName, AutoReset) +{ + std::string old_name("old"); + std::string new_name("new-name"); + if (ThreadName::DUMMY_IMPL) + { + // just make sure the API is correct + ThreadName t("test"); + return; + } + + ASSERT_EQ(ThreadName::set(old_name), true); + std::string name; + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); + + { + ThreadName threadName(new_name); + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, new_name); + } + + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); + + { + new_name.resize(std::max(512, ThreadName::BUFSIZE * 2), 'z'); + ThreadName threadName(new_name); + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(new_name.compare(0, name.size(), name), 0); + } + + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); +} From d6f93a16f4adbbf5b85de23e72e2d84cebef053b Mon Sep 17 00:00:00 2001 From: quink-black Date: Wed, 4 Aug 2021 22:37:02 +0800 Subject: [PATCH 223/790] [core] Update srtcore/threadname.h Co-authored-by: Maxim Sharabayko --- srtcore/threadname.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 6fd901aff..2b3964276 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -144,7 +144,10 @@ class ThreadName static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL; static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE; - // len should >= BUFSIZE + /// @brief Print thread ID to the provided buffer. + /// The size of the destination buffer is assumed to be at least ThreadName::BUFSIZE. + /// @param [out] output destination buffer to get thread name + /// @return true on success, false on failure static bool get(char* output) { return ThreadNameImpl::get(output); } From adba7afcea44db5a87debeb150760021108d9db1 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 4 Aug 2021 23:04:36 +0800 Subject: [PATCH 224/790] [core] Use ThreadName::BUFSIZE in CreateLogLinePrefix() --- srtcore/common.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 389933b26..0e19dc611 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -595,16 +595,20 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) { using namespace std; - char tmp_buf[512]; + SRT_STATIC_ASSERT(ThreadName::BUFSIZE >= sizeof("hh:mm:ss.") * 2, // multiply 2 for some margin + "ThreadName::BUFSIZE is too small to be used for strftime"); + char tmp_buf[ThreadName::BUFSIZE]; if ( !isset(SRT_LOGF_DISABLE_TIME) ) { // Not necessary if sending through the queue. timeval tv; - gettimeofday(&tv, 0); + gettimeofday(&tv, NULL); struct tm tm = SysLocalTime((time_t) tv.tv_sec); - strftime(tmp_buf, 512, "%X.", &tm); - serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) + { + serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + } } string out_prefix; From 6dcbaf0eb302f7eec51a4f2dcb93d3d5855276ab Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Thu, 5 Aug 2021 18:07:49 +1000 Subject: [PATCH 225/790] [build] Cross-compile android from macOS host (#2071) --- configure-data.tcl | 15 +++++++++++-- scripts/build-android/build-android | 34 ++++++++++++++++++++++++----- scripts/build-android/mkssl | 9 ++++---- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/configure-data.tcl b/configure-data.tcl index 66902324a..5c0ee2ee7 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -343,8 +343,19 @@ proc postprocess {} { # Otherwise don't set PKG_CONFIG_PATH and we'll see. } - if { $::HAVE_DARWIN && !$toolchain_changed} { - + set use_brew 0 + if { $::HAVE_DARWIN && !$toolchain_changed } { + set use_brew 1 + } + if { $use_brew } { + foreach item $::cmakeopt { + if { [string first "Android" $item] != -1 } { + set use_brew 0 + break + } + } + } + if { $use_brew } { if { $have_gnutls } { # Use gnutls explicitly, as found in brew set er [catch {exec brew info gnutls} res] diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 3d69dac0c..0d56a8f01 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -52,11 +52,35 @@ else fi fi -# Determine the path of the executing script -BASE_DIR=$(readlink -f $0 | xargs dirname) +SCRIPT_DIR=$(pwd) +HOST_TAG='unknown' +unamestr=$(uname -s) +if [ "$unamestr" = 'Linux' ]; then + SCRIPT_DIR=$(readlink -f $0 | xargs dirname) + HOST_TAG='linux-x86_64' +elif [ "$unamestr" = 'Darwin' ]; then + SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + if [ $(uname -p) = 'arm' ]; then + echo "NDK does not currently support ARM64" + exit 128 + else + HOST_TAG='darwin-x86_64' + fi +fi + +# Write files relative to current location +BASE_DIR=$(pwd) +case "${BASE_DIR}" in + *\ * ) + echo "Your path contains whitespaces, which is not supported by 'make install'." + exit 128 + ;; +esac +cd "${BASE_DIR}" if [ $ENC_LIB = 'openssl' ]; then - $BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION + echo "Building OpenSSL $OPENSSL_VERSION" + $SCRIPT_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION -d $BASE_DIR -h $HOST_TAG elif [ $ENC_LIB = 'mbedtls' ]; then if [ ! -d $BASE_DIR/mbedtls ]; then git clone https://github.com/ARMmbed/mbedtls mbedtls @@ -83,13 +107,13 @@ for build_target in $BUILD_TARGETS; do mkdir -p $JNI_DIR if [ $ENC_LIB = 'mbedtls' ]; then - $BASE_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target + $SCRIPT_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so fi git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index d0305fab7..4e9d0df28 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -1,6 +1,6 @@ #!/bin/sh -while getopts n:o:a:t: option +while getopts n:o:a:t:d:h: option do case "${option}" in @@ -8,13 +8,14 @@ do o) OPENSSL_VERSION=${OPTARG};; a) API_LEVEL=${OPTARG};; t) BUILD_TARGETS=${OPTARG};; + d) OUT_DIR=${OPTARG};; + h) HOST_TAG=${OPTARG};; *) twentytwo=${OPTARG};; esac done BUILD_DIR=/tmp/openssl_android_build -OUT_DIR=$(pwd) if [ ! -d openssl-${OPENSSL_VERSION} ] then @@ -29,7 +30,7 @@ cd openssl-${OPENSSL_VERSION} || exit 128 ##### Prepare Files ##### -sed -i 's/.*-mandroid.*//' Configurations/15-android.conf +sed -i.bak 's/.*-mandroid.*//' Configurations/15-android.conf patch -p1 -N < Date: Thu, 5 Aug 2021 12:49:20 +0200 Subject: [PATCH 226/790] [core] Fixed uninitialized DST socket ID in SHUTDOWN ctrl message --- srtcore/api.h | 11 +++++++++-- srtcore/core.cpp | 9 ++++++--- srtcore/core.h | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index 5750c9db1..e1ba244f3 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -325,14 +325,21 @@ friend class CRendezvousQueue; CEPoll& epoll_ref() { return m_EPoll; } private: -// void init(); - /// Generates a new socket ID. This function starts from a randomly /// generated value (at initialization time) and goes backward with /// with next calls. The possible values come from the range without /// the SRTGROUP_MASK bit, and the group bit is set when the ID is /// generated for groups. It is also internally checked if the /// newly generated ID isn't already used by an existing socket or group. + /// + /// Socket ID value range. + /// - [0]: reserved for handshake procedure. If the destination Socket ID is 0 + /// (destination Socket ID unknown) the packet will be sent to the listening socket + /// or to a socket that is in the rendezvous connection phase. + /// - [1; 2 ^ 30): single socket ID range. + /// - (2 ^ 30; 2 ^ 31): group socket ID range. Effectively any positive number + /// from [1; 2 ^ 30) with bit 30 set to 1. Bit 31 is zero. + /// The most significant bit 31 (sign bit) is left unused so that checking for a value <= 0 identifies an invalid socket ID. /// /// @param group The socket id should be for socket group. /// @return The new socket ID. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cf01d9e35..a96ca4c47 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -243,6 +243,7 @@ void srt::CUDT::construct() // Will be reset to 0 for HSv5, this value is important for HSv4. m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; + m_PeerID = 0; m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -3784,9 +3785,9 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // m_RejectReason already set at worker_ProcessAddressedPacket. LOGC(cnlog.Warn, - log << "processAsyncConnectRequest: REJECT reported from HS processing:" + log << "processAsyncConnectRequest: REJECT reported from HS processing: " << srt_rejectreason_str(m_RejectReason) - << "- not processing further"); //; REQ-TIME LOW"); XXX ? + << " - not processing further"); // m_tsLastReqTime = steady_clock::time_point(); XXX ? return false; } @@ -5957,7 +5958,7 @@ bool srt::CUDT::closeInternal() { if (!m_bShutdown) { - HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer"); + HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer @" << m_PeerID); sendCtrl(UMSG_SHUTDOWN); } @@ -7615,6 +7616,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp break; case UMSG_SHUTDOWN: // 101 - Shutdown + if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. + break; ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); diff --git a/srtcore/core.h b/srtcore/core.h index 9a1ad01f0..6b8472c64 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1029,7 +1029,7 @@ class CUDT private: // Trace struct CoreStats { - time_point tsStartTime; // timestamp when the UDT entity is started + time_point tsStartTime; // timestamp when the UDT entity is started int64_t sentTotal; // total number of sent data packets, including retransmissions int64_t sentUniqTotal; // total number of sent data packets, excluding rexmit and filter control int64_t recvTotal; // total number of received packets From 0fca8741ce319066c1abe62757e1fa7edeeb9d58 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 5 Aug 2021 12:50:13 +0200 Subject: [PATCH 227/790] [tests] Fixed build warning: unused parameter --- test/test_socket_options.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index bf37513ce..eb0b05912 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -349,7 +349,7 @@ bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char { try { const ValueType val = linb::any_cast(inval); - CheckSetSockOpt(entry, sock, val, SRT_ERROR, "[Caller, invalid val]"); + CheckSetSockOpt(entry, sock, val, SRT_ERROR, sock_name); } catch (const linb::bad_any_cast&) { @@ -454,17 +454,18 @@ TEST_F(TestSocketOptions, InvalidVals) // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation for (const auto& entry : g_test_matrix_options) { + const char* desc = "[Caller, invalid val]"; if (entry.dflt_val.type() == typeid(bool)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else if (entry.dflt_val.type() == typeid(int)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else if (entry.dflt_val.type() == typeid(int64_t)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else { From 747f288e77e88a9e0f7db70f2fa8477868fa5928 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 6 Aug 2021 13:44:36 +0200 Subject: [PATCH 228/790] [core] Check if CryptoControl exists in createSrtHandshake(..). If it does not, it will lead to a crash, reported in #1979. --- srtcore/core.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a96ca4c47..63e4fadd9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1725,6 +1725,16 @@ bool srt::CUDT::createSrtHandshake( HLOGC(cnlog.Debug, log << "createSrtHandshake: " << (m_config.CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); + + if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP)) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(cnlog.Error, log << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." + << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting + << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); + return false; + } + if (srtkm_cmd == SRT_CMD_KMREQ) { bool have_any_keys = false; @@ -1753,7 +1763,6 @@ bool srt::CUDT::createSrtHandshake( { offset += ra_size + 1; ra_size = fillHsExtKMRSP(p + offset - 1, kmdata, kmdata_wordsize); - } else { From f0aa009f0d591c1f74e6d01f84055b69b56a7d04 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 9 Aug 2021 10:47:38 +0200 Subject: [PATCH 229/790] [core] Renamed congestion control event handling functions --- srtcore/congctl.cpp | 52 +++++++++++++++++++++++++++------------------ srtcore/core.cpp | 38 ++++++++++++++++----------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 4612e6852..3b0f923a0 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -81,7 +81,7 @@ class LiveCC: public SrtCongestionControlBase m_zSndAvgPayloadSize = m_zMaxPayloadSize; m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) - m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator + m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize); @@ -92,11 +92,11 @@ class LiveCC: public SrtCongestionControlBase // from receiving thread. parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize)); - /* - * Readjust the max SndPeriod onACK (and onTimeout) - */ - parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer)); - parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck)); + // + // Adjust the max SndPeriod onACK and onTimeout. + // + parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO)); + parent->ConnectSignal(TEV_ACK, SSLOT(onAck)); } bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE @@ -153,17 +153,22 @@ class LiveCC: public SrtCongestionControlBase HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize); } - void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var) + /// @brief On RTO event update an inter-packet send interval. + /// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO. + void onRTO(ETransmissionEvent , EventVariant var) { - if ( var.get() != TEV_CHT_INIT ) + if (var.get() != TEV_CHT_INIT ) updatePktSndPeriod(); } - void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant ) + /// @brief Handle an incoming ACK event. + /// Mainly updates a send interval between packets relying on the maximum BW limit. + void onAck(ETransmissionEvent, EventVariant ) { updatePktSndPeriod(); } + /// @brief Updates a send interval between packets relying on the maximum BW limit. void updatePktSndPeriod() { // packet = payload + header @@ -289,9 +294,9 @@ class FileCC : public SrtCongestionControlBase m_dCWndSize = 16; m_dPktSndPeriod = 1; - parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod)); - parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod)); - parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize)); + parent->ConnectSignal(TEV_ACK, SSLOT(onACK)); + parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(onLossReport)); + parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO)); HLOGC(cclog.Debug, log << "Creating FileCC"); } @@ -305,10 +310,11 @@ class FileCC : public SrtCongestionControlBase return true; } + /// Tells if an early ACK is needed (before the next Full ACK happening every 10ms). + /// In FileCC, treat non-full-payload as an end-of-message (stream) + /// and request ACK to be sent immediately. bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE { - // For FileCC, treat non-full-buffer situation as an end-of-message situation; - // request ACK to be sent immediately. if (pkt.getLength() < m_parent->maxPayloadSize()) { // This is not a regular fixed size packet... @@ -329,9 +335,10 @@ class FileCC : public SrtCongestionControlBase } private: - - // SLOTS - void updateSndPeriod(ETransmissionEvent, EventVariant arg) + /// Handle icoming ACK event. + /// In slow start stage increase CWND. Leave slow start once maximum CWND is reached. + /// In congestion avoidance stage adjust inter packet send interval value to achieve maximum rate. + void onACK(ETransmissionEvent, EventVariant arg) { const int ack = arg.get(); @@ -455,9 +462,10 @@ class FileCC : public SrtCongestionControlBase } - // When a lossreport has been received, it might be due to having - // reached the available bandwidth limit. Slowdown to avoid further losses. - void slowdownSndPeriod(ETransmissionEvent, EventVariant arg) + /// When a lossreport has been received, it might be due to having + /// reached the available bandwidth limit. Slowdown to avoid further losses. + /// Leave the slow start stage if it was active. + void onLossReport(ETransmissionEvent, EventVariant arg) { const int32_t* losslist = arg.get_ptr(); size_t losslist_size = arg.get_len(); @@ -559,7 +567,9 @@ class FileCC : public SrtCongestionControlBase } } - void speedupToWindowSize(ETransmissionEvent, EventVariant arg) + /// @brief On retransmission timeout leave slow start stage if it was active. + /// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO. + void onRTO(ETransmissionEvent, EventVariant arg) { ECheckTimerStage stg = arg.get(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 63e4fadd9..c11e66064 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10899,21 +10899,21 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) { - /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. - * - * LATEREXMIT is only used with FileCC. - * The mode is triggered when some time has passed since the last ACK from - * the receiver, while there is still some unacknowledged data in the sender's buffer, - * and the loss list is empty. - * - * FASTREXMIT is only used with LiveCC. - * The mode is triggered if the receiver does not send periodic NAK reports, - * when some time has passed since the last ACK from the receiver, - * while there is still some unacknowledged data in the sender's buffer. - * - * In case the above conditions are met, the unacknowledged packets - * in the sender's buffer will be added to loss list and retransmitted. - */ + // There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. + // + // LATEREXMIT is only used with FileCC. + // The RTO is triggered when some time has passed since the last ACK from + // the receiver, while there is still some unacknowledged data in the sender's buffer, + // and the loss list is empty at the moment of RTO (nothing to retransmit yet). + // + // FASTREXMIT is only used with LiveCC. + // The RTO is triggered if the receiver is not configured to send periodic NAK reports, + // when some time has passed since the last ACK from the receiver, + // while there is still some unacknowledged data in the sender's buffer. + // + // In case the above conditions are met, the unacknowledged packets + // in the sender's buffer will be added to the SND loss list and retransmitted. + // const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); @@ -10926,19 +10926,19 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) if (m_pSndBuffer->getCurrBufSize() <= 0) return; - const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; - const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; + const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; // FileCC + const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; // LiveCC // If the receiver will send periodic NAK reports, then FASTREXMIT (live) is inactive. // TODO: Probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off. if (is_fastrexmit && m_bPeerNakReport) return; - // Schedule for retransmission IF: + // Schedule a retransmission IF: // - there are packets in flight (getFlightSpan() > 0); // - in case of LATEREXMIT (File Mode): the sender loss list is empty // (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track). - // - in case of FASTREXMIT (Live Mode): there is the latency constraint, therefore + // - in case of FASTREXMIT (Live Mode): the RTO (rtt_syn) was triggered, therefore // schedule unacknowledged packets for retransmission regardless of the loss list emptiness. if (getFlightSpan() > 0 && (!is_laterexmit || m_pSndLossList->getLossLength() == 0)) { From 252337c129c011cada22dcd1772beae40579989d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 9 Aug 2021 10:54:58 +0200 Subject: [PATCH 230/790] [core] Moved congestion control into the srt namespace --- srtcore/congctl.cpp | 9 ++++++--- srtcore/congctl.h | 11 ++++------- srtcore/socketconfig.cpp | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 3b0f923a0..2301dfc68 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -34,10 +34,11 @@ #include "logging.h" using namespace std; -using namespace srt; using namespace srt::sync; using namespace srt_logging; +namespace srt { + SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent) { m_parent = parent; @@ -399,7 +400,7 @@ class FileCC : public SrtCongestionControlBase else { double inc = 0; - const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point + const int loss_bw = static_cast(2 * (1000000 / m_dLastDecPeriod)); // 2 times last loss point const int bw_pktps = min(loss_bw, m_parent->bandwidth()); int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod); @@ -500,7 +501,7 @@ class FileCC : public SrtCongestionControlBase m_bLoss = true; // TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo()); - const int pktsInFlight = m_parent->SRTT() / m_dPktSndPeriod; + const int pktsInFlight = static_cast(m_parent->SRTT() / m_dPktSndPeriod); const int numPktsLost = m_parent->sndLossLength(); const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0; @@ -656,3 +657,5 @@ SrtCongestion::~SrtCongestion() { dispose(); } + +} // namespace srt diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 0ad835783..b957dbdd0 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -17,10 +17,9 @@ #include namespace srt { - class CUDT; -} -class SrtCongestionControlBase; +class CUDT; +class SrtCongestionControlBase; typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent); class SrtCongestion @@ -131,9 +130,7 @@ class SrtCongestion }; }; -namespace srt { - class CPacket; -} +class CPacket; class SrtCongestionControlBase { @@ -224,6 +221,6 @@ class SrtCongestionControlBase }; - +} // namespace srt #endif diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index a62881cd7..820a4334e 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -570,7 +570,7 @@ struct CSrtConfigSetter if (val == "vod") val = "file"; - bool res = SrtCongestion::exists(val); + bool res = srt::SrtCongestion::exists(val); if (!res) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); From d7dcf0cab7e7a44d9c2daf387f809585fffc715f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 11 Aug 2021 16:41:34 +0200 Subject: [PATCH 231/790] [core] Check if socket is still open before replying to HS --- srtcore/core.cpp | 2 ++ srtcore/queue.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c11e66064..7c0f3acf5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3769,6 +3769,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, bool status = true; ScopedLock cg(m_ConnectionLock); + if (!m_bOpened) // Check the socket has not been closed before already. + return false; if (cst == CONN_RENDEZVOUS) { diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 2b0fd2c74..03ba97e47 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -989,11 +989,12 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst for (vector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); - /* - * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. - * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) - * and may crash on next pass. - */ + // + // Setting m_bConnecting to false, and need to remove the socket from the rendezvous queue + // because the next CUDT::close will not remove it from the queue when m_bConnecting = false, + // and may crash on next pass. + // + // TODO: maybe lock i->u->m_ConnectionLock? i->u->m_bConnecting = false; remove(i->u->m_SocketID); From 7728b7000fb720d1d974f1d24db07b4ce82adf95 Mon Sep 17 00:00:00 2001 From: Laurent Bigonville Date: Thu, 12 Aug 2021 15:10:13 +0200 Subject: [PATCH 232/790] [core] Prefer the endian.h header file from glibc if available On the Debian kfreebsd port, both endian.h and sys/endian.h are available. After discussing with the kfreebsd port maintainer and since this is userspace related, we should prefer the one from the glibc See: #2066 --- srtcore/utilities.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index b1f21fbb2..60a286ec0 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -57,7 +57,7 @@ written by #endif -#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) +#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) # include @@ -115,7 +115,7 @@ written by # include -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) # include From f1c96d9868c4b9daa05d07bc8dca1b681cad314b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 12 Aug 2021 11:58:56 +0200 Subject: [PATCH 233/790] [core] Check Win QPC frequency is non-zero --- srtcore/sync_posix.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 960a1dfad..c44fe86c2 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -86,6 +86,12 @@ static int64_t get_cpu_frequency() if (QueryPerformanceFrequency(&ccf)) { frequency = ccf.QuadPart / 1000000; // counts per microsecond + if (frequency == 0) + { + LOGC(inlog.Warn, log << "Win QPC frequency of " << ccf.QuadPart + << " counts/s is below the required 1 us accuracy. Please consider using C++11 timing (-DENABLE_STDCXX_SYNC=ON) instead."); + frequency = 1; // set back to 1 to avoid division by zero. + } } else { From 6ca1e0daa59ec0d7983ab8da002a5f7281bd2a2b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 12:16:00 +0200 Subject: [PATCH 234/790] [tests] Fixed possible TestIPv6 hangups if connection fails --- test/test_ipv6.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 820288fa8..69a51bfbb 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -48,11 +48,14 @@ class TestIPv6 { sockaddr_any sa (family); sa.hport(m_listen_port); - ASSERT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); + EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; - ASSERT_NE(srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa), SRT_ERROR); + const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + if (connect_res == SRT_ERROR) + srt_close(m_listener_sock); PrintAddresses(m_caller_sock, "CALLER"); } @@ -61,7 +64,7 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { - ASSERT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; + EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; } @@ -70,16 +73,23 @@ class TestIPv6 sockaddr_any sc1; SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); - EXPECT_NE(accepted_sock, SRT_INVALID_SOCK); + EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); + if (accepted_sock == SRT_INVALID_SOCK) { + return sockaddr_any(); + } PrintAddresses(accepted_sock, "ACCEPTED"); sockaddr_any sn; EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); - - int32_t ipv6_zero [] = {0, 0, 0, 0}; - EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0) - << "EMPTY address in srt_getsockname"; + EXPECT_NE(sn.get_addr(), nullptr); + + if (sn.get_addr() != nullptr) + { + const int32_t ipv6_zero[] = { 0, 0, 0, 0 }; + EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0) + << "EMPTY address in srt_getsockname"; + } srt_close(accepted_sock); return sn; From b2141220f5e441a9cee99f8d80b2a32bb38f7577 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 11:48:32 +0200 Subject: [PATCH 235/790] [core] Fixed int64_t to long type cast --- srtcore/api.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d38442590..264192155 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2171,8 +2171,8 @@ int srt::CUDTUnited::select( { const steady_clock::time_point entertime = steady_clock::now(); - const long timeo_us = timeout - ? timeout->tv_sec * 1000000 + timeout->tv_usec + const int64_t timeo_us = timeout + ? static_cast(timeout->tv_sec) * 1000000 + timeout->tv_usec : -1; const steady_clock::duration timeo(microseconds_from(timeo_us)); @@ -2285,7 +2285,7 @@ int srt::CUDTUnited::selectEx( { const steady_clock::time_point entertime = steady_clock::now(); - const long timeo_us = msTimeOut >= 0 + const int64_t timeo_us = msTimeOut >= 0 ? msTimeOut * 1000 : -1; const steady_clock::duration timeo(microseconds_from(timeo_us)); From bc5a6429473648a70396a9acc229e04fd5e4d0a9 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 14:37:20 +0200 Subject: [PATCH 236/790] [tests] Fixed range loop construct warning --- test/test_socket_options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index eb0b05912..d010de510 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -345,7 +345,7 @@ bool CheckMaxValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* des template bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char* sock_name) { - for (const auto inval : entry.invalid_vals) + for (const auto& inval : entry.invalid_vals) { try { const ValueType val = linb::any_cast(inval); From ae11c18453fd46f15606e601b7232f48e3d4a464 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 11:45:35 +0200 Subject: [PATCH 237/790] [core] CUDTSocket now owns a member CUDT class (composition). --- srtcore/api.cpp | 244 ++++++++++++++++++++++------------------------ srtcore/api.h | 31 +++++- srtcore/core.cpp | 20 ++-- srtcore/group.cpp | 18 ++-- 4 files changed, 164 insertions(+), 149 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 264192155..7f6d7ecaa 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -96,10 +96,6 @@ void srt::CUDTSocket::construct() srt::CUDTSocket::~CUDTSocket() { - - delete m_pUDT; - m_pUDT = NULL; - releaseMutex(m_AcceptLock); releaseCond(m_AcceptCond); releaseMutex(m_ControlLock); @@ -113,11 +109,11 @@ SRT_SOCKSTATUS srt::CUDTSocket::getStatus() // In this case m_bConnected is also false. Both checks are required to avoid hitting // a regular state transition from CONNECTING to CONNECTED. - if (m_pUDT->m_bBroken) + if (m_UDT.m_bBroken) return SRTS_BROKEN; // Connecting timed out - if ((m_Status == SRTS_CONNECTING) && !m_pUDT->m_bConnecting && !m_pUDT->m_bConnected) + if ((m_Status == SRTS_CONNECTING) && !m_UDT.m_bConnecting && !m_UDT.m_bConnected) return SRTS_BROKEN; return m_Status; @@ -128,10 +124,10 @@ void srt::CUDTSocket::breakSocket_LOCKED() { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. - m_pUDT->m_bBroken = true; - m_pUDT->m_iBrokenCounter = 0; + m_UDT.m_bBroken = true; + m_UDT.m_iBrokenCounter = 0; HLOGC(smlog.Debug, log << "@" << m_SocketID << " CLOSING AS SOCKET"); - m_pUDT->closeInternal(); + m_UDT.closeInternal(); setClosed(); } @@ -148,16 +144,16 @@ void srt::CUDTSocket::setClosed() void srt::CUDTSocket::setBrokenClosed() { - m_pUDT->m_iBrokenCounter = 60; - m_pUDT->m_bBroken = true; + m_UDT.m_iBrokenCounter = 60; + m_UDT.m_bBroken = true; setClosed(); } bool srt::CUDTSocket::readReady() { - if (m_pUDT->m_bConnected && m_pUDT->m_pRcvBuffer->isRcvDataReady()) + if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) return true; - if (m_pUDT->m_bListening) + if (m_UDT.m_bListening) { return m_QueuedSockets.size() > 0; } @@ -167,14 +163,14 @@ bool srt::CUDTSocket::readReady() bool srt::CUDTSocket::writeReady() const { - return (m_pUDT->m_bConnected - && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) + return (m_UDT.m_bConnected + && (m_UDT.m_pSndBuffer->getCurrBufSize() < m_UDT.m_config.iSndBufSize)) || broken(); } bool srt::CUDTSocket::broken() const { - return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; + return m_UDT.m_bBroken || !m_UDT.m_bConnected; } //////////////////////////////////////////////////////////////////////////////// @@ -239,7 +235,7 @@ int srt::CUDTUnited::startup() ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return 1; + return 1; // Global initialization code #ifdef _WIN32 @@ -290,10 +286,10 @@ int srt::CUDTUnited::cleanup() ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) - return 0; + return 0; if (!m_bGCStatus) - return 0; + return 0; m_bClosing = true; // NOTE: we can do relaxed signaling here because @@ -436,7 +432,6 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) try { ns = new CUDTSocket; - ns->m_pUDT = new CUDT(ns); } catch (...) { @@ -455,8 +450,8 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) } ns->m_Status = SRTS_INIT; ns->m_ListenSocket = 0; - ns->m_pUDT->m_SocketID = ns->m_SocketID; - ns->m_pUDT->m_pCache = m_pCache; + ns->core().m_SocketID = ns->m_SocketID; + ns->core().m_pCache = m_pCache; try { @@ -507,7 +502,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // if this connection has already been processed if ((ns = locatePeer(peer, w_hs.m_iID, w_hs.m_iISN)) != NULL) { - if (ns->m_pUDT->m_bBroken) + if (ns->core().m_bBroken) { // last connection from the "peer" address has been broken ns->setClosed(); @@ -523,15 +518,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p << "newConnection: located a WORKING peer @" << w_hs.m_iID << " - ADAPTING."); - w_hs.m_iISN = ns->m_pUDT->m_iISN; - w_hs.m_iMSS = ns->m_pUDT->MSS(); - w_hs.m_iFlightFlagSize = ns->m_pUDT->m_config.iFlightFlagSize; + w_hs.m_iISN = ns->core().m_iISN; + w_hs.m_iMSS = ns->core().MSS(); + w_hs.m_iFlightFlagSize = ns->core().m_config.iFlightFlagSize; w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; // Report the original UDT because it will be // required to complete the HS data for conclusion response. - w_acpu = ns->m_pUDT; + w_acpu = &ns->core(); return 0; @@ -554,8 +549,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p try { - ns = new CUDTSocket; - ns->m_pUDT = new CUDT(ns, *(ls->m_pUDT)); + ns = new CUDTSocket(*ls); // No need to check the peer, this is the address from which the request has come. ns->m_PeerAddr = peer; } @@ -567,7 +561,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p return -1; } - ns->m_pUDT->m_RejectReason = SRT_REJ_UNKNOWN; // pre-set a universal value + ns->core().m_RejectReason = SRT_REJ_UNKNOWN; // pre-set a universal value try { @@ -585,13 +579,13 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p } ns->m_ListenSocket = listen; - ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->core().m_SocketID = ns->m_SocketID; ns->m_PeerID = w_hs.m_iID; ns->m_iISN = w_hs.m_iISN; HLOGC(cnlog.Debug, log << "newConnection: DATA: lsnid=" << listen - << " id=" << ns->m_pUDT->m_SocketID - << " peerid=" << ns->m_pUDT->m_PeerID + << " id=" << ns->core().m_SocketID + << " peerid=" << ns->core().m_PeerID << " ISN=" << ns->m_iISN); int error = 0; @@ -621,11 +615,11 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p m_Sockets[ns->m_SocketID] = ns; } - if (ls->m_pUDT->m_cbAcceptHook) + if (ls->core().m_cbAcceptHook) { - if (!ls->m_pUDT->runAcceptHook(ns->m_pUDT, peer.get(), w_hs, hspkt)) + if (!ls->core().runAcceptHook(&ns->core(), peer.get(), w_hs, hspkt)) { - w_error = ns->m_pUDT->m_RejectReason; + w_error = ns->core().m_RejectReason; error = 1; goto ERR_ROLLBACK; @@ -633,15 +627,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p } // bind to the same addr of listening socket - ns->m_pUDT->open(); + ns->core().open(); updateListenerMux(ns, ls); - ns->m_pUDT->acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); + ns->core().acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); } catch (...) { // Extract the error that was set in this new failed entity. - w_error = ns->m_pUDT->m_RejectReason; + w_error = ns->core().m_RejectReason; error = 1; goto ERR_ROLLBACK; } @@ -651,11 +645,11 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // copy address information of local node // Precisely, what happens here is: // - Get the IP address and port from the system database - ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((ns->m_SelfAddr)); + ns->core().m_pSndQueue->m_pChannel->getSockAddr((ns->m_SelfAddr)); // - OVERWRITE just the IP address itself by a value taken from piSelfIP // (the family is used exactly as the one taken from what has been returned // by getsockaddr) - CIPAddress::pton((ns->m_SelfAddr), ns->m_pUDT->m_piSelfIP, peer); + CIPAddress::pton((ns->m_SelfAddr), ns->core().m_piSelfIP, peer); { // protect the m_PeerRec structure (and group existence) @@ -757,7 +751,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p /* SETUP HERE IF NEEDED - ns->m_pUDT->m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); + ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); */ } else @@ -783,7 +777,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p HLOGC(cnlog.Debug, log << "ACCEPT: new socket @" << ns->m_SocketID << " submitted for acceptance"); // acknowledge users waiting for new connections on the listening socket - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_ACCEPT, true); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); CGlobEvent::triggerEvent(); @@ -803,7 +797,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // acknowledge INTERNAL users waiting for new connections on the listening socket // that are reported when a new socket is connected within an already connected group. - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_UPDATE, true); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); CGlobEvent::triggerEvent(); } @@ -823,7 +817,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p #endif SRTSOCKET id = ns->m_SocketID; - ns->m_pUDT->closeInternal(); + ns->core().closeInternal(); ns->setClosed(); // The mapped socket should be now unmapped to preserve the situation that @@ -862,7 +856,7 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ try { CUDTSocket* s = locateSocket(lsn, ERH_THROW); - s->m_pUDT->installAcceptHook(hook, opaq); + s->core().installAcceptHook(hook, opaq); } catch (CUDTException& e) { @@ -891,7 +885,7 @@ int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_ } #endif CUDTSocket* s = locateSocket(u, ERH_THROW); - s->m_pUDT->installConnectHook(hook, opaq); + s->core().installConnectHook(hook, opaq); } catch (CUDTException& e) { @@ -927,12 +921,12 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) if (s->m_Status != SRTS_INIT) throw CUDTException(MJ_NOTSUP, MN_NONE, 0); - s->m_pUDT->open(); + s->core().open(); updateMux(s, name); s->m_Status = SRTS_OPENED; // copy address information of local node - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); return 0; } @@ -956,12 +950,12 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->m_pUDT->open(); + s->core().open(); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; // copy address information of local node - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); return 0; } @@ -998,14 +992,14 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // [[using assert(s->m_Status == OPENED)]]; // listen is not supported in rendezvous connection setup - if (s->m_pUDT->m_config.bRendezvous) + if (s->core().m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); s->m_uiBackLog = backlog; // [[using assert(s->m_Status == OPENED)]]; // (still, unchanged) - s->m_pUDT->setListenState(); // propagates CUDTException, + s->core().setListenState(); // propagates CUDTException, // if thrown, remains in OPENED state if so. s->m_Status = SRTS_LISTENING; @@ -1080,7 +1074,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int } // no "accept" in rendezvous connection setup - if (ls->m_pUDT->m_config.bRendezvous) + if (ls->core().m_config.bRendezvous) { LOGC(cnlog.Fatal, log << "CUDTUnited::accept: RENDEZVOUS flag passed through check in srt_listen when it set listen state"); // This problem should never happen because `srt_listen` function should have @@ -1098,7 +1092,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int UniqueLock accept_lock(ls->m_AcceptLock); CSync accept_sync(ls->m_AcceptCond, accept_lock); - if ((ls->m_Status != SRTS_LISTENING) || ls->m_pUDT->m_bBroken) + if ((ls->m_Status != SRTS_LISTENING) || ls->core().m_bBroken) { // This socket has been closed. accepted = true; @@ -1110,7 +1104,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int ls->m_QueuedSockets.erase(b); accepted = true; } - else if (!ls->m_pUDT->m_config.bSynRecving) + else if (!ls->core().m_config.bSynRecving) { accepted = true; } @@ -1119,13 +1113,13 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int accept_sync.wait(); if (ls->m_QueuedSockets.empty()) - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_ACCEPT, false); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, false); } if (u == CUDT::INVALID_SOCK) { // non-blocking receiving, no connection available - if (!ls->m_pUDT->m_config.bSynRecving) + if (!ls->core().m_config.bSynRecving) { LOGC(cnlog.Error, log << "srt_accept: no pending connection available at the moment"); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); @@ -1150,7 +1144,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int // and the already accepted socket has successfully joined // the mirror group. If so, RETURN THE GROUP ID, not the socket ID. #if ENABLE_EXPERIMENTAL_BONDING - if (ls->m_pUDT->m_config.iGroupConnect == 1 && s->m_GroupOf) + if (ls->core().m_config.iGroupConnect == 1 && s->m_GroupOf) { // Put a lock to protect the group against accidental deletion // in the meantime. @@ -1370,7 +1364,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i if (pg->m_cbConnectHook) { // Derive the connect hook by the socket, if set on the group - ns->m_pUDT->m_cbConnectHook = pg->m_cbConnectHook; + ns->core().m_cbConnectHook = pg->m_cbConnectHook; } SRT_SocketOptionObject* config = targets[tii].config; @@ -1494,7 +1488,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // be probably still in use to exchange information about // packets assymetrically lost. But for no other purpose. /* - ns->m_pUDT->m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); + ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); */ int isn = g.currentSchedSequence(); @@ -1508,14 +1502,14 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } // Set it the groupconnect option, as all in-group sockets should have. - ns->m_pUDT->m_config.iGroupConnect = 1; + ns->core().m_config.iGroupConnect = 1; // Every group member will have always nonblocking // (this implies also non-blocking connect/accept). // The group facility functions will block when necessary // using epoll_wait. - ns->m_pUDT->m_config.bSynRecving = false; - ns->m_pUDT->m_config.bSynSending = false; + ns->core().m_config.bSynRecving = false; + ns->core().m_config.bSynSending = false; HLOGC(aclog.Debug, log << "groupConnect: NOTIFIED AS PENDING @" << sid << " both read and write"); // If this socket is not to block the current connect process, @@ -1832,7 +1826,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i if (s->m_Status == SRTS_INIT) { - if (s->m_pUDT->m_config.bRendezvous) + if (s->core().m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); // If bind() was done first on this socket, then the @@ -1840,7 +1834,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i // same thing as bind() does, just with empty address so that // the binding parameters are autoselected. - s->m_pUDT->open(); + s->core().open(); sockaddr_any autoselect_sa (target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). @@ -1878,7 +1872,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i { // record peer address s->m_PeerAddr = target_addr; - s->m_pUDT->startConnect(target_addr, forced_isn); + s->core().startConnect(target_addr, forced_isn); } catch (CUDTException& e) // Interceptor, just to change the state. { @@ -1962,23 +1956,21 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) int srt::CUDTUnited::close(CUDTSocket* s) { - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); - + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing from listening, closing CUDT)"); - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); - - const bool synch_close_snd = s->m_pUDT->m_config.bSynSending; + const bool synch_close_snd = s->core().m_config.bSynSending; SRTSOCKET u = s->m_SocketID; if (s->m_Status == SRTS_LISTENING) { - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) return 0; s->m_tsClosureTimeStamp = steady_clock::now(); - s->m_pUDT->m_bBroken = true; + s->core().m_bBroken = true; // Change towards original UDT: // Leave all the closing activities for garbageCollect to happen, @@ -1989,8 +1981,8 @@ int srt::CUDTUnited::close(CUDTSocket* s) // be unable to bind to this port that the about-to-delete listener // is currently occupying (due to blocked slot in the RcvQueue). - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing listener immediately)"); - s->m_pUDT->notListening(); + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing listener immediately)"); + s->core().notListening(); // broadcast all "accept" waiting CSync::lock_broadcast(s->m_AcceptCond, s->m_AcceptLock); @@ -2002,10 +1994,10 @@ int srt::CUDTUnited::close(CUDTSocket* s) // may block INDEFINITELY. As long as it's acceptable to block the // call to srt_close(), and all functions in all threads where this // very socket is used, this shall not block the central database. - s->m_pUDT->closeInternal(); + s->core().closeInternal(); // synchronize with garbage collection. - HLOGC(smlog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->m_pUDT->CONID() << ". Acquiring GLOBAL control lock"); + HLOGC(smlog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->core().CONID() << ". Acquiring GLOBAL control lock"); ScopedLock manager_cg(m_GlobControlLock); // since "s" is located before m_GlobControlLock, locate it again in case // it became invalid @@ -2052,7 +2044,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) HLOGC(smlog.Debug, log << "@" << u << " GLOBAL CLOSING: sync-waiting for releasing sender resources..."); for (;;) { - CSndBuffer* sb = s->m_pUDT->m_pSndBuffer; + CSndBuffer* sb = s->core().m_pSndBuffer; // Disconnected from buffer - nothing more to check. if (!sb) @@ -2080,7 +2072,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } if (!isgone) { - isgone = !s->m_pUDT->m_bOpened; + isgone = !s->core().m_bOpened; } if (isgone) { @@ -2131,7 +2123,7 @@ void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_ if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) + if (!s->core().m_bConnected || s->core().m_bBroken) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); const int len = s->m_PeerAddr.size(); @@ -2152,7 +2144,7 @@ void srt::CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_ if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); if (s->m_Status == SRTS_INIT) @@ -2306,7 +2298,7 @@ int srt::CUDTUnited::selectEx( { CUDTSocket* s = locateSocket(*i); - if ((!s) || s->m_pUDT->m_bBroken || (s->m_Status == SRTS_CLOSED)) + if ((!s) || s->core().m_bBroken || (s->m_Status == SRTS_CLOSED)) { if (exceptfds) { @@ -2318,10 +2310,10 @@ int srt::CUDTUnited::selectEx( if (readfds) { - if ((s->m_pUDT->m_bConnected - && s->m_pUDT->m_pRcvBuffer->isRcvDataReady() + if ((s->core().m_bConnected + && s->core().m_pRcvBuffer->isRcvDataReady() ) - || (s->m_pUDT->m_bListening + || (s->core().m_bListening && (s->m_QueuedSockets.size() > 0))) { readfds->push_back(s->m_SocketID); @@ -2331,9 +2323,9 @@ int srt::CUDTUnited::selectEx( if (writefds) { - if (s->m_pUDT->m_bConnected - && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() - < s->m_pUDT->m_config.iSndBufSize)) + if (s->core().m_bConnected + && (s->core().m_pSndBuffer->getCurrBufSize() + < s->core().m_config.iSndBufSize)) { writefds->push_back(s->m_SocketID); ++ count; @@ -2393,7 +2385,7 @@ int srt::CUDTUnited::epoll_add_usock( int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) { int ret = m_EPoll.update_usock(eid, s->m_SocketID, events); - s->m_pUDT->addEPoll(eid); + s->core().addEPoll(eid); return ret; } @@ -2434,7 +2426,7 @@ int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) // Needed internal access! int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { - return epoll_remove_entity(eid, s->m_pUDT); + return epoll_remove_entity(eid, &s->core()); } #if ENABLE_EXPERIMENTAL_BONDING @@ -2461,7 +2453,7 @@ int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { s = locateSocket(u); if (s) - return epoll_remove_entity(eid, s->m_pUDT); + return epoll_remove_entity(eid, &s->core()); } LOGC(ealog.Error, log << "remove_usock: @" << u @@ -2623,7 +2615,7 @@ void srt::CUDTUnited::checkBrokenSockets() CUDTSocket* s = i->second; // check broken connection - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) { if (s->m_Status == SRTS_LISTENING) { @@ -2635,21 +2627,21 @@ void srt::CUDTUnited::checkBrokenSockets() continue; } } - else if ((s->m_pUDT->m_pRcvBuffer != NULL) + 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->m_pUDT->m_pRcvBuffer->isRcvDataAvailable()) + && s->core().m_pRcvBuffer->isRcvDataAvailable()) { - const int bc = s->m_pUDT->m_iBrokenCounter.load(); + const int bc = s->core().m_iBrokenCounter.load(); if (bc > 0) { // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): // %d\n", i->first); // if there is still data in the receiver buffer, wait longer - s->m_pUDT->m_iBrokenCounter.store(bc - 1); + s->core().m_iBrokenCounter.store(bc - 1); continue; } } @@ -2688,16 +2680,16 @@ void srt::CUDTUnited::checkBrokenSockets() j != m_ClosedSockets.end(); ++ j) { // HLOGF(smlog.Debug, "checking CLOSED socket: %d\n", j->first); - if (!is_zero(j->second->m_pUDT->m_tsLingerExpiration)) + if (!is_zero(j->second->core().m_tsLingerExpiration)) { // asynchronous close: - if ((!j->second->m_pUDT->m_pSndBuffer) - || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) - || (j->second->m_pUDT->m_tsLingerExpiration <= steady_clock::now())) + if ((!j->second->core().m_pSndBuffer) + || (0 == j->second->core().m_pSndBuffer->getCurrBufSize()) + || (j->second->core().m_tsLingerExpiration <= steady_clock::now())) { HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << j->second->m_SocketID); - j->second->m_pUDT->m_tsLingerExpiration = steady_clock::time_point(); - j->second->m_pUDT->m_bClosing = true; + j->second->core().m_tsLingerExpiration = steady_clock::time_point(); + j->second->core().m_bClosing = true; j->second->m_tsClosureTimeStamp = steady_clock::now(); } } @@ -2708,7 +2700,7 @@ void srt::CUDTUnited::checkBrokenSockets() const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; if (closed_ago > seconds_from(1)) { - CRNode* rnode = j->second->m_pUDT->m_pRNode; + CRNode* rnode = j->second->core().m_pRNode; if (!rnode || !rnode->m_bOnList) { HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " @@ -2744,11 +2736,11 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // still be under processing in the sender/receiver worker // threads. If that's the case, SKIP IT THIS TIME. The // socket will be checked next time the GC rollover starts. - CSNode* sn = s->m_pUDT->m_pSNode; + CSNode* sn = s->core().m_pSNode; if (sn && sn->m_iHeapLoc != -1) return; - CRNode* rn = s->m_pUDT->m_pRNode; + CRNode* rn = s->core().m_pRNode; if (rn && rn->m_bOnList) return; @@ -2804,14 +2796,14 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) * remains forever causing epoll_wait to unblock continuously for inexistent * sockets. Get rid of all events for this socket. */ - m_EPoll.update_events(u, s->m_pUDT->m_sPollID, + m_EPoll.update_events(u, s->core().m_sPollID, SRT_EPOLL_IN|SRT_EPOLL_OUT|SRT_EPOLL_ERR, false); // delete this one m_ClosedSockets.erase(i); HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); - s->m_pUDT->closeInternal(); + s->core().closeInternal(); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; @@ -2850,7 +2842,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { - w_m.m_mcfg = s->m_pUDT->m_config; + w_m.m_mcfg = s->core().m_config; w_m.m_iIPversion = af; w_m.m_iRefCount = 1; w_m.m_iID = s->m_SocketID; @@ -2858,8 +2850,8 @@ void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) { - w_s->m_pUDT->m_pSndQueue = fw_sm.m_pSndQueue; - w_s->m_pUDT->m_pRcvQueue = fw_sm.m_pRcvQueue; + w_s->core().m_pSndQueue = fw_sm.m_pSndQueue; + w_s->core().m_pRcvQueue = fw_sm.m_pRcvQueue; w_s->m_iMuxID = fw_sm.m_iID; sockaddr_any sa; fw_sm.m_pChannel->getSockAddr((sa)); @@ -2869,7 +2861,7 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) { - return m.m_mcfg.bReuseAddr && m.m_mcfg == s->m_pUDT->m_config; + return m.m_mcfg.bReuseAddr && m.m_mcfg == s->core().m_config; } void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) @@ -2922,14 +2914,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // Still, for ANY you need either the same family, or open // for families. - if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->m_pUDT->m_config.iIpV6Only) + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->core().m_config.iIpV6Only) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with existing IPv6 wildcard binding: " << sa.str()); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } - if ((m.m_mcfg.iIpV6Only == 0 || s->m_pUDT->m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + if ((m.m_mcfg.iIpV6Only == 0 || s->core().m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str() @@ -3026,7 +3018,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; m.m_pRcvQueue->init( - 32, s->m_pUDT->maxPayloadSize(), m.m_iIPversion, 1024, + 32, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); // Rewrite the port here, as it might be only known upon return @@ -3127,8 +3119,8 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) { // reuse the existing multiplexer ++ mux->m_iRefCount; - s->m_pUDT->m_pSndQueue = mux->m_pSndQueue; - s->m_pUDT->m_pRcvQueue = mux->m_pRcvQueue; + s->core().m_pSndQueue = mux->m_pSndQueue; + s->core().m_pRcvQueue = mux->m_pRcvQueue; s->m_iMuxID = mux->m_iID; return true; } @@ -3745,8 +3737,8 @@ int srt::CUDT::getsockopt( } #endif - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->getOpt(optname, (pw_optval), (*pw_optlen)); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } catch (const CUDTException& e) @@ -3777,8 +3769,8 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt } #endif - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->setOpt(optname, optval, optlen); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.setOpt(optname, optval, optlen); return 0; } catch (const CUDTException& e) @@ -3889,8 +3881,8 @@ int64_t srt::CUDT::sendfile( { try { - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - return udt->sendfile(ifs, offset, size, block); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + return udt.sendfile(ifs, offset, size, block); } catch (const CUDTException& e) { @@ -4237,8 +4229,8 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta try { - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->bstats(perf, clear, instantaneous); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.bstats(perf, clear, instantaneous); return 0; } catch (const CUDTException& e) @@ -4281,7 +4273,7 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { - return s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; + return &s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); } catch (const CUDTException& e) { diff --git a/srtcore/api.h b/srtcore/api.h index e1ba244f3..a1bfbffd7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -76,6 +76,8 @@ namespace srt { class CUDT; +/// @brief Class CUDTSocket is a control layer on top of the CUDT core functionality layer. +/// CUDTSocket owns CUDT. class CUDTSocket { public: @@ -89,7 +91,26 @@ class CUDTSocket , m_GroupOf() #endif , m_iISN(0) - , m_pUDT(NULL) + , m_UDT(this) + , m_AcceptCond() + , m_AcceptLock() + , m_uiBackLog(0) + , m_iMuxID(-1) + { + construct(); + } + + CUDTSocket(const CUDTSocket& ancestor) + : m_Status(SRTS_INIT) + , m_SocketID(0) + , m_ListenSocket(0) + , m_PeerID(0) +#if ENABLE_EXPERIMENTAL_BONDING + , m_GroupMemberData() + , m_GroupOf() +#endif + , m_iISN(0) + , m_UDT(this, ancestor.m_UDT) , m_AcceptCond() , m_AcceptLock() , m_uiBackLog(0) @@ -125,8 +146,10 @@ class CUDTSocket int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port - CUDT* m_pUDT; //< pointer to the UDT entity +private: + CUDT m_UDT; //< internal SRT socket logic +public: std::set m_QueuedSockets; //< set of connections waiting for accept() sync::Condition m_AcceptCond; //< used to block "accept" call @@ -149,7 +172,8 @@ class CUDTSocket sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect - CUDT& core() { return *m_pUDT; } + CUDT& core() { return m_UDT; } + const CUDT& core() const { return m_UDT; } static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { @@ -188,7 +212,6 @@ class CUDTSocket bool broken() const; private: - CUDTSocket(const CUDTSocket&); CUDTSocket& operator=(const CUDTSocket&); }; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7c0f3acf5..c887218d9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4670,8 +4670,8 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // the local port must be correctly assigned BEFORE CUDT::startConnect(), // otherwise if startConnect() fails, the multiplexer cannot be located // by garbage collection and will cause leak - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); - CIPAddress::pton((s->m_SelfAddr), s->m_pUDT->m_piSelfIP, m_PeerAddr); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + CIPAddress::pton((s->m_SelfAddr), s->core().m_piSelfIP, m_PeerAddr); //int token = -1; #if ENABLE_EXPERIMENTAL_BONDING @@ -11154,10 +11154,10 @@ void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { CUDTSocket *s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return -1; - CSndBuffer *b = s->m_pUDT->m_pSndBuffer; + CSndBuffer *b = s->core().m_pSndBuffer; if (!b) return -1; @@ -11177,32 +11177,32 @@ int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) int srt::CUDT::rejectReason(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return SRT_REJ_UNKNOWN; - return s->m_pUDT->m_RejectReason; + return s->core().m_RejectReason; } int srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); if (value < SRT_REJC_PREDEFINED) return APIError(MJ_NOTSUP, MN_INVAL); - s->m_pUDT->m_RejectReason = value; + s->core().m_RejectReason = value; return 0; } int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); - return count_microseconds(s->m_pUDT->m_stats.tsStartTime.time_since_epoch()); + return count_microseconds(s->core().m_stats.tsStartTime.time_since_epoch()); } bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f2f99201b..2c9bf5f6d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -231,7 +231,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) gli_t end = m_Group.end(); if (m_iMaxPayloadSize == -1) { - int plsize = data.ps->m_pUDT->OPT_PayloadSize(); + int plsize = data.ps->core().OPT_PayloadSize(); HLOGC(gmlog.Debug, log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); @@ -2738,13 +2738,13 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady continue; // Skip the entity that has reported this - if (cu == gi->ps->m_pUDT) + if (cu == &gi->ps->core()) continue; steady_clock::time_point this_timebase; steady_clock::duration this_udrift(0); bool wrp = false; - gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); + gi->ps->core().m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); udrift = std::min(udrift, this_udrift); steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); @@ -2775,7 +2775,7 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady if (gi->laststatus != SRTS_CONNECTED) continue; - gi->ps->m_pUDT->m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); + gi->ps->core().m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); } } @@ -3094,7 +3094,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // probability that the link will be recognized as IDLE on the // reception side ASAP. int32_t arg = 1; - w_d->ps->m_pUDT->sendCtrl(UMSG_KEEPALIVE, &arg); + w_d->ps->core().sendCtrl(UMSG_KEEPALIVE, &arg); } } @@ -4411,9 +4411,9 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) HLOGC(grlog.Debug, log << "updateLatestRcv: BACKUP group, updating from active link @" << s->m_SocketID << " with %" - << s->m_pUDT->m_iRcvLastSkipAck); + << s->core().m_iRcvLastSkipAck); - CUDT* source = s->m_pUDT; + CUDT* source = &s->core(); vector targets; UniqueLock lg(m_GroupLock); @@ -4448,13 +4448,13 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } // Sanity check - if (!gi->ps->m_pUDT->m_bConnected) + if (!gi->ps->core().m_bConnected) { HLOGC(grlog.Debug, log << "grp: IPE: NOT updating rcv-seq on @" << gi->id << " - IDLE BUT NOT CONNECTED"); continue; } - targets.push_back(gi->ps->m_pUDT); + targets.push_back(&gi->ps->core()); } lg.unlock(); From 399e8bf69fde85bb107d30babf21402cf6789a2e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 10:53:12 +0200 Subject: [PATCH 238/790] [tests] Test FileUpload: find unused UDP port (#2086) --- test/test_file_transmission.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 790555eb7..8150ca3f2 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -45,17 +45,35 @@ TEST(Transmission, FileUpload) sa_lsn.sin_addr.s_addr = INADDR_ANY; sa_lsn.sin_port = htons(5555); + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (int port = 5000; port <= 5555; ++port) + { + sa_lsn.sin_port = htons(port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << port << "\n"; + break; + } + + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); + srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); int optval = 0; int optlen = sizeof optval; ASSERT_EQ(srt_getsockflag(sock_lsn, SRTO_SNDBUF, &optval, &optlen), 0); - size_t filesize = 7 * optval; + const size_t filesize = 7 * optval; { std::cout << "WILL CREATE source file with size=" << filesize << " (= 7 * " << optval << "[sndbuf])\n"; std::ofstream outfile("file.source", std::ios::out | std::ios::binary); - ASSERT_EQ(!!outfile, true); + ASSERT_EQ(!!outfile, true) << srt_getlasterror_str(); srand(time(0)); @@ -96,6 +114,7 @@ TEST(Transmission, FileUpload) ASSERT_NE(n, SRT_ERROR); if (n == 0) { + std::cerr << "Received 0 bytes, breaking.\n"; break; } @@ -110,7 +129,7 @@ TEST(Transmission, FileUpload) sockaddr_in sa = sockaddr_in(); sa.sin_family = AF_INET; - sa.sin_port = htons(5555); + sa.sin_port = sa_lsn.sin_port; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_connect(sock_clr, (sockaddr*)&sa, sizeof(sa)); @@ -126,8 +145,8 @@ TEST(Transmission, FileUpload) size_t shift = 0; while (n > 0) { - int st = srt_send(sock_clr, buf.data()+shift, n); - ASSERT_GT(st, 0); + const int st = srt_send(sock_clr, buf.data()+shift, n); + ASSERT_GT(st, 0) << srt_getlasterror_str(); n -= st; shift += st; From 0925d6891a9f1226de547293c3dd614f4df209cb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 15:38:18 +0200 Subject: [PATCH 239/790] [docs] Improved a note about srt_recv(..) and payload size (#2080) --- docs/API/API-functions.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index d59ce481e..58b729fad 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1885,8 +1885,13 @@ to be returned does not fit in the buffer, nothing will be received and the error is reported. 3. In **live mode**, the function behaves as in **file/message mode**, although the -number of bytes retrieved will be at most the size of `SRTO_PAYLOADSIZE`. In this mode, -however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) +number of bytes retrieved will be at most the maximum payload of one MTU. +The [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) value configured by the sender +is not negotiated, and not known to the receiver. +The [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) value set on the SRT receiver +is mainly used for heuristics. However, the receiver is prepared to receive +the whole MTU as configured with [`SRTO_MSS`](API-socket-options.md#SRTO_MSS). +In this mode, however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP), the message will be received only when its time to play has come, and until then it will be kept in the receiver buffer. Also, when the time to play has come for a message that is next to From 7e2d82bab90fc1dfddd312ebeddb153cc186ace3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 09:59:27 +0200 Subject: [PATCH 240/790] [apps] Fixed loop over OptionScheme (#2089) --- apps/apputil.cpp | 2 +- apps/apputil.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index bf808a95d..611b99976 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -251,7 +251,7 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector } // Find the key in the scheme. If not found, treat it as ARG_NONE. - for (auto s: scheme) + for (const auto& s: scheme) { if (s.names().count(current_key)) { diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 4f2b84bf7..5bac461e0 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -220,7 +220,7 @@ struct OptionScheme OptionScheme(const OptionName& id, Args tp); - const std::set& names(); + const std::set& names() const; }; struct OptionName @@ -265,7 +265,7 @@ struct OptionName }; inline OptionScheme::OptionScheme(const OptionName& id, Args tp): pid(&id), type(tp) {} -inline const std::set& OptionScheme::names() { return pid->names; } +inline const std::set& OptionScheme::names() const { return pid->names; } template inline typename OutType::type Option(const options_t&, OutValue deflt=OutValue()) { return deflt; } From aed5f2086d2029b0b323687f6d6377ffa54ccd2f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 12:04:27 +0200 Subject: [PATCH 241/790] [build] Default STDCXX_SYNC=ON on Windows, try to use CLOCK_MONOTONIC by default on Linux. (#2088) Updated Windows build instructions (default C++11). --- CMakeLists.txt | 27 ++++++++------ docs/build/build-win.md | 16 ++++++--- scripts/haiUtil.cmake | 79 ++++++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f210ff34f..c1cb13ced 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,16 @@ if (ENABLE_DEBUG) endif() +set(ENABLE_STDCXX_SYNC_DEFAULT OFF) +set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF) +set(MONOTONIC_CLOCK_LINKLIB "") +if (MICROSOFT) + set(ENABLE_STDCXX_SYNC_DEFAULT ON) +elseif (LINUX) + test_requires_clock_gettime(ENABLE_MONOTONIC_CLOCK_DEFAULT MONOTONIC_CLOCK_LINKLIB) +endif() + + # options option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) @@ -129,8 +139,8 @@ option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX librari option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) option(ENABLE_CODE_COVERAGE "Enable code coverage reporting" OFF) -option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" OFF) -option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" OFF) +option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" ${ENABLE_MONOTONIC_CLOCK_DEFAULT}) +option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" ${ENABLE_STDCXX_SYNC_DEFAULT}) option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) @@ -265,6 +275,10 @@ if (DEFINED HAVE_INET_PTON) endif() if (ENABLE_MONOTONIC_CLOCK) + if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) + message(FATAL_ERROR "Your platform does not support CLOCK_MONOTONIC. Build with -DENABLE_MONOTONIC_CLOCK=OFF.") + endif() + set (WITH_EXTRALIBS "${WITH_EXTRALIBS} ${MONOTONIC_CLOCK_LINKLIB}") add_definitions(-DENABLE_MONOTONIC_CLOCK=1) endif() @@ -435,6 +449,7 @@ elseif (ENABLE_STDCXX_SYNC) endif() message(STATUS "STDCXX_SYNC: ${ENABLE_STDCXX_SYNC}") +message(STATUS "MONOTONIC_CLOCK: ${ENABLE_MONOTONIC_CLOCK}") if (ENABLE_SOCK_CLOEXEC) add_definitions(-DENABLE_SOCK_CLOEXEC=1) @@ -526,14 +541,6 @@ if (USE_STATIC_LIBSTDCXX) endif() endif() -# We need clock_gettime, but on some systems this is only provided -# by librt. Check if librt is required. -if (ENABLE_MONOTONIC_CLOCK AND LINUX) - # "requires" - exits on FATAL_ERROR when clock_gettime not available - test_requires_clock_gettime(NEED_CLOCK_GETTIME) - set (WITH_EXTRALIBS "${WITH_EXTRALIBS} ${NEED_CLOCK_GETTIME}") -endif() - # This options is necessary on some systems; on a cross-ARM compiler it # has been detected, for example, that -lrt is necessary for some applications diff --git a/docs/build/build-win.md b/docs/build/build-win.md index 3f47b310b..e09175142 100644 --- a/docs/build/build-win.md +++ b/docs/build/build-win.md @@ -54,7 +54,7 @@ only unencrypted mode can be used. With the enabled SRT encryption, one of the following Crypto libraries is required: -- `OpenSSL` (default) +- `OpenSSL` (**default**) - `LibreSSL` - `MbedTLS` @@ -62,8 +62,8 @@ one of the following Crypto libraries is required: SRT as of v1.4.2 supports two threading libraries: -- `pthreads` (default) -- Standard C++ thread library available in C++11 (recommended for Windows) +- Standard C++ thread library available in C++11 (**default on Windows**) +- `pthreads` (not recommended on Windows) The `pthreads` library is provided out-of-the-box on all POSIX-based systems. On Windows it can be provided as a 3rd party library (see below). @@ -73,6 +73,10 @@ However the C++ standard thread library is recommended to be used on Windows. #### 1.3.1. VCpkg Packet Manager (optional) +Can be used to: +- build OpenSSL library (dependency of SRT). +- build pthreads library (dependency of SRT). + [vcpkg](https://github.com/microsoft/vcpkg) is a C++ library manager for Windows, Linux and MacOS. Consider its [prerequisites](https://github.com/microsoft/vcpkg/blob/master/README.md#quick-start) before proceeding. @@ -181,8 +185,8 @@ to specify the directory that will contain the LibreSSL headers and libraries. SRT can use one of these two threading libraries: -- C++11 threads (SRT v1.4.2 and above) - recommended for Windows -- `pthreads` (default) +- C++11 threads (SRT v1.4.2 and above) - recommended, default since SRT v1.4.4; +- `pthreads` (not recommended on Windows). #### 2.2.1. Using C++11 Threading @@ -193,6 +197,8 @@ Otherwise the external PThreads for Windows wrapper library is required. #### 2.2.2. Building PThreads +It is not recommended to use `pthreads` port on Windows. Consider using [C++11 instead](#221-using-c11-threading), + ##### 2.2.2.1. Using vcpkg **Note!** The `vcpkg` working directory is referenced as `VCPKG_ROOT`. diff --git a/scripts/haiUtil.cmake b/scripts/haiUtil.cmake index e161f1e5b..9e4fb4d56 100644 --- a/scripts/haiUtil.cmake +++ b/scripts/haiUtil.cmake @@ -12,11 +12,11 @@ include(CheckCXXSourceCompiles) # Useful for combinging paths function(adddirname prefix lst out_lst) - set(output) - foreach(item ${lst}) - list(APPEND output "${prefix}/${item}") - endforeach() - set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE) + set(output) + foreach(item ${lst}) + list(APPEND output "${prefix}/${item}") + endforeach() + set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE) endfunction() # Splits a version formed as "major.minor.patch" recorded in variable 'prefix' @@ -32,11 +32,11 @@ ENDMACRO(set_version_variables) # Sets given variable to 1, if the condition that follows it is satisfied. # Otherwise set it to 0. MACRO(set_if varname) - IF(${ARGN}) - SET(${varname} 1) - ELSE(${ARGN}) - SET(${varname} 0) - ENDIF(${ARGN}) + IF(${ARGN}) + SET(${varname} 1) + ELSE(${ARGN}) + SET(${varname} 0) + ENDIF(${ARGN}) ENDMACRO(set_if) FUNCTION(join_arguments outvar) @@ -80,11 +80,11 @@ MACRO(MafReadDir directory maffile) configure_file(${directory}/${maffile} dummy_${maffile}.cmake.out) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/dummy_${maffile}.cmake.out) - #message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}") - #message("DEBUG: PASSED VARIABLES:") - #foreach(DEBUG_VAR ${MAFREAD_TAGS}) - # message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}") - #endforeach() + #message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}") + #message("DEBUG: PASSED VARIABLES:") + #foreach(DEBUG_VAR ${MAFREAD_TAGS}) + # message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}") + #endforeach() # The unnamed section becomes SOURCES set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES}) @@ -186,15 +186,15 @@ MACRO(MafReadDir directory maffile) ENDFOREACH() # Final debug report - #set (ALL_VARS "") - #message("DEBUG: extracted variables:") - #foreach(DEBUG_VAR ${MAFREAD_TAGS}) - # list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}}) - #endforeach() - #list(REMOVE_DUPLICATES ALL_VARS) - #foreach(DEBUG_VAR ${ALL_VARS}) - # message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}") - #endforeach() + #set (ALL_VARS "") + #message("DEBUG: extracted variables:") + #foreach(DEBUG_VAR ${MAFREAD_TAGS}) + # list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}}) + #endforeach() + #list(REMOVE_DUPLICATES ALL_VARS) + #foreach(DEBUG_VAR ${ALL_VARS}) + # message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}") + #endforeach() ENDMACRO(MafReadDir) # NOTE: This is historical only. Not in use. @@ -214,9 +214,9 @@ MACRO(GetMafHeaders directory outvar) ENDMACRO(GetMafHeaders) function (getVarsWith _prefix _varResult) - get_cmake_property(_vars VARIABLES) - string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}") - set (${_varResult} ${_matchedVars} PARENT_SCOPE) + get_cmake_property(_vars VARIABLES) + string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}") + set (${_varResult} ${_matchedVars} PARENT_SCOPE) endfunction() function (check_testcode_compiles testcode libraries _successful) @@ -228,15 +228,18 @@ function (check_testcode_compiles testcode libraries _successful) set (CMAKE_REQUIRED_LIBRARIES ${save_required_libraries}) endfunction() -function (test_requires_clock_gettime _result) +function (test_requires_clock_gettime _enable _linklib) # This function tests if clock_gettime can be used # - at all # - with or without librt # Result will be: - # rt (if librt required) - # "" (if no extra libraries required) - # -- killed by FATAL_ERROR if clock_gettime is not available + # - CLOCK_MONOTONIC is available, link with librt: + # _enable = ON; _linklib = "-lrt". + # - CLOCK_MONOTONIC is available, link without librt: + # _enable = ON; _linklib = "". + # - CLOCK_MONOTONIC is not available: + # _enable = OFF; _linklib = "-". set (code " #include @@ -249,19 +252,23 @@ function (test_requires_clock_gettime _result) check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN) if (HAVE_CLOCK_GETTIME_IN) - message(STATUS "Checked clock_gettime(): no extra libs needed") - set (${_result} "" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: availabe, no extra libs needed") + set (${_enable} ON PARENT_SCOPE) + set (${_linklib} "" PARENT_SCOPE) return() endif() check_testcode_compiles(${code} "rt" HAVE_CLOCK_GETTIME_LIBRT) if (HAVE_CLOCK_GETTIME_LIBRT) - message(STATUS "Checked clock_gettime(): requires -lrt") - set (${_result} "-lrt" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: available, requires -lrt") + set (${_enable} ON PARENT_SCOPE) + set (${_linklib} "-lrt" PARENT_SCOPE) return() endif() - message(FATAL_ERROR "clock_gettime() is not available on this system") + set (${_enable} OFF PARENT_SCOPE) + set (${_linklib} "-" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: not available on this system") endfunction() function (parse_compiler_type wct _type _suffix) From 9475ad0ec04cd67cf7b43ddd9a53230ee09df769 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 11:08:52 +0200 Subject: [PATCH 242/790] [core] Removed redundant srt namespace scope resolution in CUDT --- srtcore/core.h | 90 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/srtcore/core.h b/srtcore/core.h index 6b8472c64..9c9a38f04 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -172,8 +172,8 @@ class CUDT typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; - typedef srt::sync::AtomicClock atomic_time_point; - typedef srt::sync::AtomicDuration atomic_duration; + typedef sync::AtomicClock atomic_time_point; + typedef sync::AtomicDuration atomic_duration; private: // constructor and desctructor void construct(); @@ -313,8 +313,8 @@ class CUDT int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } - srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } + sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } + sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } int flowWindowSize() const { return m_iFlowWindowSize; } @@ -386,7 +386,7 @@ class CUDT // So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1) // XXX Would be nice to check if local_time > m_tsStartTime, // otherwise it may go unnoticed with clock skew. - return (int32_t) srt::sync::count_microseconds(from_time - m_stats.tsStartTime); + return (int32_t) sync::count_microseconds(from_time - m_stats.tsStartTime); } void setPacketTS(CPacket& p, const time_point& local_time) @@ -398,14 +398,14 @@ class CUDT // immediately to free the socket void notListening() { - srt::sync::ScopedLock cg(m_ConnectionLock); + sync::ScopedLock cg(m_ConnectionLock); m_bListening = false; m_pRcvQueue->removeListener(this); } static int32_t generateISN() { - using namespace srt::sync; + using namespace sync; return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } @@ -420,8 +420,8 @@ class CUDT SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); - SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); - SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); + SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond); + SRTU_PROPERTY_RR(sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); /// @brief Request a socket to be broken due to too long instability (normally by a group). void breakAsUnstable() { m_bBreakAsUnstable = true; } @@ -734,30 +734,30 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - srt::sync::atomic m_bListening; // If the UDT entity is listening to connection - srt::sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed - srt::sync::atomic m_bConnected; // Whether the connection is on or off - srt::sync::atomic m_bClosing; // If the UDT entity is closing - srt::sync::atomic m_bShutdown; // If the peer side has shutdown the connection - srt::sync::atomic m_bBroken; // If the connection has been broken - srt::sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. - srt::sync::atomic m_bPeerHealth; // If the peer status is normal - srt::sync::atomic m_RejectReason; + sync::atomic m_bListening; // If the UDT entity is listening to connection + sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed + sync::atomic m_bConnected; // Whether the connection is on or off + sync::atomic m_bClosing; // If the UDT entity is closing + sync::atomic m_bShutdown; // If the peer side has shutdown the connection + sync::atomic m_bBroken; // If the connection has been broken + sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. + sync::atomic m_bPeerHealth; // If the peer status is normal + sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - srt::sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter - srt::sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second - srt::sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second + sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds - srt::sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds - srt::sync::atomic m_bIsFirstRTTReceived;// True if the first RTT sample was obtained from the ACK/ACKACK pair + sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + sync::atomic m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. - srt::sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side - srt::sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side + sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side + sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response @@ -773,7 +773,7 @@ class CUDT atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time - srt::sync::atomic m_iFlowWindowSize; // Flow control window size + sync::atomic m_iFlowWindowSize; // Flow control window size double m_dCongestionWindow; // Congestion window size private: // Timers @@ -798,8 +798,8 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending - srt::sync::atomic m_iSndLastFullAck;// Last full ACK received - srt::sync::atomic m_iSndLastAck; // Last ACK received + sync::atomic m_iSndLastFullAck; // Last full ACK received + sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) // and this is the sequence number that refers to the block at position [0]. Upon acknowledgement, @@ -809,9 +809,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - srt::sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list - srt::sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - srt::sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list + sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -873,7 +873,7 @@ class CUDT int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged int32_t m_iAckSeqNo; // Last ACK sequence number - srt::sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number + sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) int32_t m_iPeerISN; // Initial Sequence Number of the peer side @@ -884,10 +884,10 @@ class CUDT bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead - srt::sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle - srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock + 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 - srt::sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining + sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; CallbackHolder m_cbConnectHook; @@ -909,21 +909,21 @@ class CUDT private: // synchronization: mutexes and conditions - srt::sync::Mutex m_ConnectionLock; // used to synchronize connection operation + sync::Mutex m_ConnectionLock; // used to synchronize connection operation - srt::sync::Condition m_SendBlockCond; // used to block "send" call - srt::sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond + sync::Condition m_SendBlockCond; // used to block "send" call + sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond - srt::sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer + sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer // Protects access to m_iSndCurrSeqNo, m_iSndLastAck - srt::sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) + sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) - srt::sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock - srt::sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady()) + sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock + sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady()) - srt::sync::Mutex m_SendLock; // used to synchronize "send" call - srt::sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) - mutable srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics + sync::Mutex m_SendLock; // used to synchronize "send" call + sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) + mutable sync::Mutex m_StatsLock; // used to synchronize access to trace statistics void initSynch(); void destroySynch(); From 675e75de015f61c156a1c9fa50a3e8607d721e5a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 11:12:21 +0200 Subject: [PATCH 243/790] [core] Added SRT_ATTR_GUARDED_BY to some members of CUDT. Imported from #1859. Co-authored-by: Mikolaj Malecki --- srtcore/core.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/srtcore/core.h b/srtcore/core.h index 9c9a38f04..a9d2f9dc3 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -744,7 +744,7 @@ class CUDT sync::atomic m_bPeerHealth; // If the peer status is normal sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second @@ -773,6 +773,7 @@ class CUDT atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time + SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iFlowWindowSize; // Flow control window size double m_dCongestionWindow; // Congestion window size @@ -782,6 +783,7 @@ class CUDT duration m_tdACKInterval; // ACK interval duration m_tdNAKInterval; // NAK interval + SRT_ATTR_GUARDED_BY(m_RecvAckLock) atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer atomic_time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) @@ -799,6 +801,7 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending sync::atomic m_iSndLastFullAck; // Last full ACK received + SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) @@ -809,9 +812,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list - sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + sync::atomic m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list + sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -853,6 +856,8 @@ class CUDT bool m_bPeerTLPktDrop; // Enable sender late packet dropping bool m_bPeerNakReport; // Sender's peer (receiver) issues Periodic NAK Reports bool m_bPeerRexmitFlag; // Receiver supports rexmit flag in payload packets + + SRT_ATTR_GUARDED_BY(m_RecvAckLock) int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data From 4fe19fcd2718602bab508a7cc5ccd57e4dd3ccd0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 11:18:38 +0200 Subject: [PATCH 244/790] [docs] Updated RCV buffer size calculation guide. Using long long for high bitrates. --- docs/API/configuration-guidelines.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index 54d3cd15a..83693d1f1 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -87,13 +87,12 @@ where ```c++ - auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS) { const int UDPHDR_SIZE = 28; - const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; - const int targetNumPackets = targetPayloadBytes / bytesPayloadSize; - const int targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); + const long long targetPayloadBytes = static_cast(msLatency + msRTT / 2) * bpsRate / 1000 / 8; + const long long targetNumPackets = targetPayloadBytes / bytesPayloadSize; + const long long targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); return {targetNumPackets, targetSizeValue}; } From 67f70d0e6039ef0cf795e7b9f61b523dab6f17ce Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 15:46:31 +0200 Subject: [PATCH 245/790] [apps] Fixed testing apps building with GCC 4.8 --- testing/testactivemedia.hpp | 2 +- testing/testmediabase.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index b94ff9aa3..06a1cd922 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -29,7 +29,7 @@ struct Medium std::mutex buffer_lock; std::thread thr; std::condition_variable ready; - std::atomic running {false}; + std::atomic running = {false}; std::exception_ptr xp; // To catch exception thrown by a thread virtual void Runner() = 0; diff --git a/testing/testmediabase.hpp b/testing/testmediabase.hpp index 3eb16a4bb..04a85d435 100644 --- a/testing/testmediabase.hpp +++ b/testing/testmediabase.hpp @@ -11,6 +11,7 @@ #ifndef INC_SRT_COMMON_TRANMITBASE_HPP #define INC_SRT_COMMON_TRANMITBASE_HPP +#include #include #include #include From 5f37067ce077b417bafb4f4e7a0766a3f794efe8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 11:38:23 +0200 Subject: [PATCH 246/790] [apps] Workaround bad move semantics of OptionScheme. OptionScheme::pid does not own an object. --- testing/srt-test-multiplex.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 2b6b2b2af..859547069 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -464,11 +464,15 @@ int main( int argc, char** argv ) } } cleanupobj; - // Check options + const OptionName + o_loglevel = { "ll", "loglevel" }, + o_input = { "i" }, + o_output = { "o" }; + vector optargs = { - { {"ll", "loglevel"}, OptionScheme::ARG_ONE }, - { {"i"}, OptionScheme::ARG_VAR }, - { {"o"}, OptionScheme::ARG_VAR } + { o_loglevel, OptionScheme::ARG_ONE }, + { o_input, OptionScheme::ARG_VAR }, + { o_output, OptionScheme::ARG_VAR } }; map> params = ProcessOptions(argv, argc, optargs); From 74aff8287caf404cdcb7b33172412709a1a20821 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 12:44:40 +0200 Subject: [PATCH 247/790] [build] Bump version to 1.4.4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cb13ced..a52dbad49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.4.3) +set (SRT_VERSION 1.4.4) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables From 8b1be6196f109f2fb8a1a791bfb092e9ca41af36 Mon Sep 17 00:00:00 2001 From: ThibaultBee <37510686+ThibaultBee@users.noreply.github.com> Date: Mon, 23 Aug 2021 12:15:40 +0200 Subject: [PATCH 248/790] [build] Fixed ENABLE_MONOTONIC_CLOCK=ON when target is Android --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a52dbad49..242bf7e70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,8 @@ 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)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(POSIX LINUX OR DARWIN OR BSD OR (CYGWIN AND CYGWIN_USE_POSIX)) +set_if(ANDROID ${CMAKE_SYSTEM_NAME} MATCHES "ANDROID") +set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) # Not sure what to do in case of compiling by MSVC. @@ -114,7 +115,7 @@ set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF) set(MONOTONIC_CLOCK_LINKLIB "") if (MICROSOFT) set(ENABLE_STDCXX_SYNC_DEFAULT ON) -elseif (LINUX) +elseif (POSIX) test_requires_clock_gettime(ENABLE_MONOTONIC_CLOCK_DEFAULT MONOTONIC_CLOCK_LINKLIB) endif() From cbdd676aa50d4517eb634f54ece27f93894f4f0d Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Sat, 21 Aug 2021 15:50:53 +0800 Subject: [PATCH 249/790] [core] Added missing lock for isRcvDataReady() --- srtcore/group.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 2c9bf5f6d..e835453ea 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2071,11 +2071,13 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& readReady.push_back(*sockiter); } - else if (sock->core().m_pRcvBuffer->isRcvDataReady()) + else { // No read-readiness reported by epoll, but probably missed or not yet handled // as the receiver buffer is read-ready. - readReady.push_back(sock); + ScopedLock lg(sock->core().m_RcvBufferLock); + if (sock->core().m_pRcvBuffer && sock->core().m_pRcvBuffer->isRcvDataReady()) + readReady.push_back(sock); } } From ab9e69b28d2193e4f407537e2c144fdae09628a0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 25 Aug 2021 10:45:22 +0200 Subject: [PATCH 250/790] [docs] Removed a reference to YAML syntax as it is not used actually. See #2047. --- docs/features/access-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/access-control.md b/docs/features/access-control.md index 101502566..d583f795b 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -46,7 +46,7 @@ specification in POSIX: `#!`. The next two characters are: -- `:` - this marks the YAML format, the only one currently used +- `:` - marks the format of the following key-value pair syntax (the only one defined currently). - The content format, which is either: - `:` - the comma-separated keys with no nesting - `{` - like above, but nesting is allowed and must end with `}` From da706240d10332ae9bb804558823996963f7160d Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 25 Aug 2021 03:46:55 -0500 Subject: [PATCH 251/790] [build] Fix Build for Android. (#2100) Some versions of CMake set CMAKE_SYSTEM_NAME to "Android". Link the atomic library for Android targets. --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 242bf7e70..4d817261a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ 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)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(ANDROID ${CMAKE_SYSTEM_NAME} MATCHES "ANDROID") +set_if(ANDROID "${CMAKE_SYSTEM_NAME}" MATCHES "^[Aa][Nn][Dd][Rr][Oo][Ii][Dd]") set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) @@ -867,6 +867,8 @@ if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) + elseif (ANDROID) + target_link_libraries(${TARGET_srt}_shared PRIVATE atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_shared PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -901,6 +903,8 @@ if (srt_libspec_static) endif() elseif (MINGW) target_link_libraries(${TARGET_srt}_static PRIVATE wsock32 ws2_32) + elseif (ANDROID) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_static PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) From fb0987500f1fbc33ef156c13768646adcd7af7be Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Fri, 27 Aug 2021 04:51:42 -0500 Subject: [PATCH 252/790] [build] Fix build for Linux GLIBC-2.8 and earlier. (#2103) --- srtcore/epoll.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index bfce672cc..d4919ff39 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -112,11 +112,31 @@ int CEPoll::create(CEPollDesc** pout) int localid = 0; #ifdef LINUX - int flags = 0; -#if ENABLE_SOCK_CLOEXEC - flags |= EPOLL_CLOEXEC; -#endif - localid = epoll_create1(flags); + + // NOTE: epoll_create1() and EPOLL_CLOEXEC were introduced in GLIBC-2.9. + // So earlier versions of GLIBC, must use epoll_create() and set + // FD_CLOEXEC on the file descriptor returned by it after the fact. + #if defined(EPOLL_CLOEXEC) + int flags = 0; + #if ENABLE_SOCK_CLOEXEC + flags |= EPOLL_CLOEXEC; + #endif + localid = epoll_create1(flags); + #else + localid = epoll_create(1); + #if ENABLE_SOCK_CLOEXEC + if (localid != -1) + { + int fdFlags = fcntl(localid, F_GETFD); + if (fdFlags != -1) + { + fdFlags |= FD_CLOEXEC; + fcntl(localid, F_SETFD, fdFlags); + } + } + #endif + #endif + /* Possible reasons of -1 error: EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered. ENFILE: The system limit on the total number of open files has been reached. From 4fc0f317b04196b9f36c5ac46ba4f357295baa50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 30 Aug 2021 16:18:02 +0200 Subject: [PATCH 253/790] [build] Use lowercase CMAKE_SYSTEM_NAME on Android --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d817261a..ed2253b05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ 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)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(ANDROID "${CMAKE_SYSTEM_NAME}" MATCHES "^[Aa][Nn][Dd][Rr][Oo][Ii][Dd]") +set_if(ANDROID ${SYSNAME_LC} MATCHES "android") set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) From 409a40d408090c56026413e5706414f1363aa430 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Tue, 31 Aug 2021 12:02:28 +0200 Subject: [PATCH 254/790] [core] Detect pthread_getname* availability --- CMakeLists.txt | 3 ++ scripts/FindPThreadGetSetName.cmake | 82 +++++++++++++++++++++++++++++ srtcore/srt_attr_defs.h | 2 +- srtcore/threadname.h | 57 ++++++++++++++++---- 4 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 scripts/FindPThreadGetSetName.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ed2253b05..3693b5b37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,9 @@ if (DEFINED HAVE_INET_PTON) add_definitions(-DHAVE_INET_PTON=1) endif() +include(FindPThreadGetSetName) +FindPThreadGetSetName() # Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* + if (ENABLE_MONOTONIC_CLOCK) if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) message(FATAL_ERROR "Your platform does not support CLOCK_MONOTONIC. Build with -DENABLE_MONOTONIC_CLOCK=OFF.") diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake new file mode 100644 index 000000000..fa818fd73 --- /dev/null +++ b/scripts/FindPThreadGetSetName.cmake @@ -0,0 +1,82 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for pthread_getname_np(3) and pthread_setname_np(3) +# used in srtcore/threadname.h. +# +# Some BSD distros need to include for pthread_getname_np(). +# +# TODO: Some BSD distros have pthread_get_name_np() and pthread_set_name_np() +# instead of pthread_getname_np() and pthread_setname_np(). +# +# Sets: +# HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H +# HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H +# HAVE_PTHREAD_GETNAME_NP +# HAVE_PTHREAD_SETNAME_NP +# Sets as appropriate: +# add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) +# add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) +# add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) +# add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + +include(CheckSymbolExists) + +function(FindPThreadGetSetName) + + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + + unset(HAVE_PTHREAD_GETNAME_NP) + unset(HAVE_PTHREAD_GETNAME_NP PARENT_SCOPE) + unset(HAVE_PTHREAD_GETNAME_NP CACHE) + unset(HAVE_PTHREAD_SETNAME_NP) + unset(HAVE_PTHREAD_SETNAME_NP PARENT_SCOPE) + unset(HAVE_PTHREAD_SETNAME_NP CACHE) + + set(CMAKE_REQUIRED_DEFINITIONS + -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) + set(CMAKE_REQUIRED_FLAGS "-pthread") + + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") + check_symbol_exists( + pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + check_symbol_exists( + pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") + check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + endif() + check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + endif() + if (HAVE_PTHREAD_GETNAME_NP) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) + endif() + if (HAVE_PTHREAD_SETNAME_NP) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + endif() + + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_FLAGS) + +endfunction(FindPThreadGetSetName) diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 82e6e5e3a..00d35e069 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -116,7 +116,7 @@ used by SRT library internally. #define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS #else -#if defined(__clang__) +#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5) #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 2b3964276..e392f6d8e 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -16,24 +16,60 @@ written by #ifndef INC_SRT_THREADNAME_H #define INC_SRT_THREADNAME_H -#if defined(__APPLE__) || defined(__linux__) -#if defined(__linux__) -#include +// NOTE: +// HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H +// HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H +// HAVE_PTHREAD_GETNAME_NP +// HAVE_PTHREAD_GETNAME_NP +// Are detected and set in ../CMakeLists.txt. +// OS Availability of pthread_getname_np(..) and pthread_setname_np(..):: +// MacOS(10.6) +// iOS(3.2) +// AIX(7.1) +// FreeBSD(version?), OpenBSD(Version?) +// Linux-GLIBC(GLIBC-2.12). +// Linux-MUSL(MUSL-1.1.20 Partial Implementation. See below). +// MINGW-W64(4.0.6) + +#if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ + || defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + #include + #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ + && !defined(HAVE_PTHREAD_GETNAME_NP) + #define HAVE_PTHREAD_GETNAME_NP 1 + #endif + #if defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) \ + && !defined(HAVE_PTHREAD_SETNAME_NP) + #define HAVE_PTHREAD_SETNAME_NP 1 + #endif #endif -#include +#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ + || defined(__linux__) + // NOTE: + // Linux pthread_getname_np() and pthread_setname_np() became available + // in GLIBC-2.12 and later. + // Some Linux runtimes do not have pthread_getname_np(), but have + // pthread_setname_np(), for instance MUSL at least as of v1.1.20. + // So using the prctl() for Linux is more portable. + #if defined(__linux__) + #include + #endif + #include #endif #include #include #include +#include "common.h" #include "sync.h" class ThreadName { -#if defined(__APPLE__) || defined(__linux__) +#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ + || defined(__linux__) class ThreadNameImpl { @@ -47,8 +83,7 @@ class ThreadName // since Linux 2.6.11. The buffer should allow space for up to 16 // bytes; the returned string will be null-terminated. return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; -#elif defined(__APPLE__) - // since macos(10.6), ios(3.2) +#elif defined(HAVE_PTHREAD_GETNAME_NP) return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0; #else #error "unsupported platform" @@ -57,14 +92,18 @@ class ThreadName static bool set(const char* name) { + SRT_ASSERT(name != NULL); #if defined(__linux__) // The name can be up to 16 bytes long, including the terminating // null byte. (If the length of the string, including the terminating // null byte, exceeds 16 bytes, the string is silently truncated.) return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; -#elif defined(__APPLE__) - // since macos(10.6), ios(3.2) +#elif defined(HAVE_PTHREAD_SETNAME_NP) + #if defined(__APPLE__) return pthread_setname_np(name) == 0; + #else + return pthread_setname_np(pthread_self(), name) == 0; + #endif #else #error "unsupported platform" #endif From 5e75c3221f227e86bbd4400fdc91d98f55cd0455 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 31 Aug 2021 16:27:03 +0200 Subject: [PATCH 255/790] [core] Placed ThreadName class inside srt namespace --- apps/srt-tunnel.cpp | 18 +++++++++--------- srtcore/threadname.h | 4 ++++ test/test_threadname.cpp | 2 ++ testing/srt-test-multiplex.cpp | 10 +++++----- testing/srt-test-relay.cpp | 4 ++-- testing/testactivemedia.cpp | 4 ++-- testing/testactivemedia.hpp | 2 +- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 2c4aacfb3..fd25fb998 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -223,17 +223,17 @@ class Engine Engine(Tunnel* p, Medium* m1, Medium* m2, const std::string& nid) : #ifdef HAVE_FULL_CXX11 - media {m1, m2}, + media {m1, m2}, #endif - parent_tunnel(p), nameid(nid) + parent_tunnel(p), nameid(nid) { #ifndef HAVE_FULL_CXX11 - // MSVC is not exactly C++11 compliant and complains around - // initialization of an array. - // Leaving this method of initialization for clarity and - // possibly more preferred performance. - media[0] = m1; - media[1] = m2; + // MSVC is not exactly C++11 compliant and complains around + // initialization of an array. + // Leaving this method of initialization for clarity and + // possibly more preferred performance. + media[0] = m1; + media[1] = m2; #endif } @@ -241,7 +241,7 @@ class Engine { Verb() << "START: " << media[DIR_IN]->uri() << " --> " << media[DIR_OUT]->uri(); std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); - ThreadName tn(thrn.c_str()); + srt::ThreadName tn(thrn.c_str()); thr = thread([this]() { Worker(); }); } diff --git a/srtcore/threadname.h b/srtcore/threadname.h index e392f6d8e..a3dbb475e 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -65,6 +65,8 @@ written by #include "common.h" #include "sync.h" +namespace srt { + class ThreadName { @@ -217,4 +219,6 @@ class ThreadName } }; +} // namespace srt + #endif diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp index f9b1d1d8c..67301f97b 100644 --- a/test/test_threadname.cpp +++ b/test/test_threadname.cpp @@ -4,6 +4,8 @@ #include "gtest/gtest.h" #include "threadname.h" +using namespace srt; + TEST(ThreadName, GetSet) { std::string name("getset"); diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 859547069..e2d5c8b89 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -154,14 +154,14 @@ struct MediumPair applog.Note() << sout.str(); } } - catch (Source::ReadEOF& x) + catch (const Source::ReadEOF&) { applog.Note() << "EOS - closing media for loop: " << name; src->Close(); tar->Close(); applog.Note() << "CLOSED: " << name; } - catch (std::runtime_error& x) + catch (const std::runtime_error& x) { applog.Note() << "INTERRUPTED: " << x.what(); src->Close(); @@ -196,7 +196,7 @@ class MediaBase med.name = name; // Ok, got this, so we can start transmission. - ThreadName tn(thread_name.c_str()); + srt::ThreadName tn(thread_name.c_str()); med.runner = thread( [&med]() { med.TransmissionLoop(); }); return med; @@ -577,7 +577,7 @@ int main( int argc, char** argv ) SrtModel m(up.host(), iport, up.parameters()); - ThreadName::set("main"); + srt::ThreadName::set("main"); // Note: for input, there must be an exactly defined // number of sources. The loop rolls up to all these sources. @@ -615,7 +615,7 @@ int main( int argc, char** argv ) break; } - ThreadName::set("main"); + srt::ThreadName::set("main"); } applog.Note() << "All local stream definitions covered. Waiting for interrupt/broken all connections."; diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 1214125b7..323718b51 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -392,7 +392,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin void SrtMainLoop::InputRunner() { - ThreadName::set("InputRN"); + srt::ThreadName::set("InputRN"); // An extra thread with a loop that reads from the external input // and writes into the SRT medium. When echoback mode is used, // this thread isn't started at all and instead the SRT reading @@ -438,7 +438,7 @@ void SrtMainLoop::run() std::ostringstream tns; tns << "Input:" << this; - ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str().c_str()); m_input_thr = thread([this] { try { InputRunner(); diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 765d22ef6..53f42a9a1 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -3,7 +3,7 @@ void SourceMedium::Runner() { - ThreadName::set("SourceRN"); + srt::ThreadName::set("SourceRN"); Verb() << VerbLock << "Starting SourceMedium: " << this; for (;;) @@ -63,7 +63,7 @@ MediaPacket SourceMedium::Extract() void TargetMedium::Runner() { - ThreadName::set("TargetRN"); + srt::ThreadName::set("TargetRN"); auto on_return_set = OnReturnSet(running, false); Verb() << VerbLock << "Starting TargetMedium: " << this; for (;;) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index 06a1cd922..fbb2f9dc8 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -58,7 +58,7 @@ struct Medium running = true; std::ostringstream tns; tns << typeid(*this).name() << ":" << this; - ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str().c_str()); thr = thread( [this] { RunnerBase(); } ); } From df95ecb66be7ecffdfbe2f0b54b77ef7b7184d36 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 1 Sep 2021 13:50:36 -0500 Subject: [PATCH 256/790] [core] Fix atomic for MacOS, FreeBSD, GCC<4.7, Clang<6 (#2104) --- CMakeLists.txt | 7 ++ srtcore/atomic.h | 151 ++++++++++++++++++++++++++++++++++------- srtcore/atomic_clock.h | 91 +++++++++++++++++++++++++ srtcore/sync.h | 74 ++------------------ 4 files changed, 233 insertions(+), 90 deletions(-) create mode 100644 srtcore/atomic_clock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3693b5b37..c5cfa1394 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,13 @@ option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) +# NOTE: Use ATOMIC_USE_SRT_SYNC_MUTEX and will override the auto-detection of the +# Atomic implemetation in srtcore/atomic.h. +option(ATOMIC_USE_SRT_SYNC_MUTEX "Use srt::sync::Mutex to Implement Atomics" OFF) +if (ATOMIC_USE_SRT_SYNC_MUTEX) + add_definitions(-DATOMIC_USE_SRT_SYNC_MUTEX=1) +endif() + set(TARGET_srt "srt" CACHE STRING "The name for the SRT library") # Use application-defined group reader diff --git a/srtcore/atomic.h b/srtcore/atomic.h index 5cedd396d..caec84fe1 100644 --- a/srtcore/atomic.h +++ b/srtcore/atomic.h @@ -61,16 +61,54 @@ line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED #endif -#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) -#define ATOMIC_USE_GCC_INTRINSICS +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + // NOTE: Defined at the top level. +#elif defined(__APPLE__) && (__cplusplus >= 201103L) + // NOTE: Does support c++11 std::atomic, but the compiler may or + // may not support GCC atomic intrinsics. So go ahead and use the + // std::atomic implementation. + #define ATOMIC_USE_CPP11_ATOMIC +#elif (defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)) \ + || defined(__xlc__) + // NOTE: Clang <6 does not support GCC __atomic_* intrinsics. I am unsure + // about Clang6. Since Clang sets __GNUC__ and __GNUC_MINOR__ of this era + // to <4.5, older Clang will catch the setting below to use the + // POSIX Mutex Implementation. + #define ATOMIC_USE_GCC_INTRINSICS +#elif defined(__GNUC__) \ + && ( (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) ) + // NOTE: The __atomic_* family of intrisics were introduced in GCC-4.7.0. + // NOTE: This follows #if defined(__clang__), because most if, not all, + // versions of Clang define __GNUC__ and __GNUC_MINOR__ but often define + // them to 4.4 or an even earlier version. Most of the newish versions + // of Clang also support GCC Atomic Intrisics even if they set GCC version + // macros to <4.7. + #define ATOMIC_USE_GCC_INTRINSICS +#elif defined(__GNUC__) && !defined(ATOMIC_USE_SRT_SYNC_MUTEX) + // NOTE: GCC compiler built-ins for atomic operations are pure + // compiler extensions prior to GCC-4.7 and were grouped into the + // the __sync_* family of functions. GCC-4.7, both the c++11 and C11 + // standards had been finalized, and GCC updated their built-ins to + // better reflect the new memory model and the new functions grouped + // into the __atomic_* family. Also the memory models were defined + // differently, than in pre 4.7. + // TODO: PORT to the pre GCC-4.7 __sync_* intrinsics. In the meantime use + // the POSIX Mutex Implementation. + #define ATOMIC_USE_SRT_SYNC_MUTEX 1 #elif defined(_MSC_VER) -#define ATOMIC_USE_MSVC_INTRINSICS -#include "atomic_msvc.h" + #define ATOMIC_USE_MSVC_INTRINSICS + #include "atomic_msvc.h" #elif __cplusplus >= 201103L -#define ATOMIC_USE_CPP11_ATOMIC -#include + #define ATOMIC_USE_CPP11_ATOMIC #else -#error Unsupported compiler / system. + #error Unsupported compiler / system. +#endif +// Include any necessary headers for the selected Atomic Implementation. +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + #include "sync.h" +#endif +#if defined(ATOMIC_USE_CPP11_ATOMIC) + #include #endif namespace srt { @@ -82,31 +120,62 @@ class atomic { sizeof(T) == 8, "Only types of size 1, 2, 4 or 8 are supported"); - atomic() : value_(static_cast(0)) {} + atomic() + : value_(static_cast(0)) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + , mutex_() +#endif + { + // No-Op + } + + explicit atomic(const T value) + : value_(value) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + , mutex_() +#endif + { + // No-Op + } - explicit atomic(const T value) : value_(value) {} + ~atomic() + { + // No-Op + } /// @brief Performs an atomic increment operation (value + 1). /// @returns The new value of the atomic object. T operator++() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = ++value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::increment(&value_); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return ++value_; +#else + #error "Implement Me." #endif } /// @brief Performs an atomic decrement operation (value - 1). /// @returns The new value of the atomic object. T operator--() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = --value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::decrement(&value_); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return --value_; +#else + #error "Implement Me." #endif } @@ -119,7 +188,16 @@ class atomic { /// @param new_val The new value to write to the atomic object. /// @returns True if new_value was written to the atomic object. bool compare_exchange(const T expected_val, const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + bool result = false; + if (expected_val == value_) + { + value_ = new_val; + result = true; + } + return result; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) T e = expected_val; return __atomic_compare_exchange_n( &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); @@ -127,9 +205,11 @@ class atomic { const T old_val = msvc::interlocked::compare_exchange(&value_, new_val, expected_val); return (old_val == expected_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) T e = expected_val; return value_.compare_exchange_weak(e, new_val); +#else + #error "Implement Me." #endif } @@ -140,12 +220,17 @@ class atomic { /// /// @param new_val The new value to write to the atomic object. void store(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + value_ = new_val; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) (void)msvc::interlocked::exchange(&value_, new_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) value_.store(new_val); +#else + #error "Implement Me." #endif } @@ -153,13 +238,19 @@ class atomic { /// @note Be careful about how this is used, since any operations on the /// returned value are inherently non-atomic. T load() const { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) // TODO(m): Is there a better solution for MSVC? return value_; -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return value_; +#else + #error "Implement Me." #endif } @@ -171,12 +262,19 @@ class atomic { /// @param new_val The new value to write to the atomic object. /// @returns the old value. T exchange(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = value_; + value_ = new_val; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::exchange(&value_, new_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return value_.exchange(new_val); +#else + #error "Implement Me." #endif } @@ -190,10 +288,17 @@ class atomic { } private: -#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + T value_; + mutable Mutex mutex_; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) volatile T value_; -#else +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + volatile T value_; +#elif defined(ATOMIC_USE_CPP11_ATOMIC) std::atomic value_; +#else + #error "Implement Me. (value_ type)" #endif ATOMIC_DISALLOW_COPY(atomic) diff --git a/srtcore/atomic_clock.h b/srtcore/atomic_clock.h new file mode 100644 index 000000000..e01012313 --- /dev/null +++ b/srtcore/atomic_clock.h @@ -0,0 +1,91 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_SYNC_ATOMIC_CLOCK_H +#define INC_SRT_SYNC_ATOMIC_CLOCK_H + +#include "sync.h" +#include "atomic.h" + +namespace srt +{ +namespace sync +{ + +template +class AtomicDuration +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicDuration() ATR_NOEXCEPT : dur(0) {} + + duration_type load() + { + int64_t val = dur.load(); + return duration_type(val); + } + + void store(const duration_type& d) + { + dur.store(d.count()); + } + + AtomicDuration& operator=(const duration_type& s) + { + dur = s.count(); + return *this; + } + + operator duration_type() const + { + return duration_type(dur); + } +}; + +template +class AtomicClock +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicClock() ATR_NOEXCEPT : dur(0) {} + + time_point_type load() const + { + int64_t val = dur.load(); + return time_point_type(duration_type(val)); + } + + void store(const time_point_type& d) + { + dur.store(uint64_t(d.time_since_epoch().count())); + } + + AtomicClock& operator=(const time_point_type& s) + { + dur = s.time_since_epoch().count(); + return *this; + } + + operator time_point_type() const + { + return time_point_type(duration_type(dur.load())); + } +}; + +} // namespace sync +} // namespace srt + +#endif // INC_SRT_SYNC_ATOMIC_CLOCK_H diff --git a/srtcore/sync.h b/srtcore/sync.h index d78aed980..00633da06 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -23,7 +23,6 @@ #define SRT_SYNC_CLOCK_STR "STDCXX_STEADY" #else #include -#include "atomic.h" // Defile clock type to use #ifdef IA32 @@ -233,72 +232,11 @@ inline Duration operator*(const int& lhs, const Duration -class AtomicDuration -{ - atomic dur; - typedef typename Clock::duration duration_type; - typedef typename Clock::time_point time_point_type; -public: - - AtomicDuration() ATR_NOEXCEPT : dur(0) {} - - duration_type load() - { - int64_t val = dur.load(); - return duration_type(val); - } - - void store(const duration_type& d) - { - dur.store(d.count()); - } - - AtomicDuration& operator=(const duration_type& s) - { - dur = s.count(); - return *this; - } - - operator duration_type() const - { - return duration_type(dur); - } -}; - -template -class AtomicClock -{ - atomic dur; - typedef typename Clock::duration duration_type; - typedef typename Clock::time_point time_point_type; -public: - - AtomicClock() ATR_NOEXCEPT : dur(0) {} - - time_point_type load() const - { - int64_t val = dur.load(); - return time_point_type(duration_type(val)); - } - - void store(const time_point_type& d) - { - dur.store(uint64_t(d.time_since_epoch().count())); - } - - AtomicClock& operator=(const time_point_type& s) - { - dur = s.time_since_epoch().count(); - return *this; - } - - operator time_point_type() const - { - return time_point_type(duration_type(dur.load())); - } -}; - +// NOTE: Moved the following class definitons to "atomic_clock.h" +// template +// class AtomicDuration; +// template +// class AtomicClock; /////////////////////////////////////////////////////////////////////////////// // @@ -939,4 +877,6 @@ int genRandomInt(int minVal, int maxVal); } // namespace sync } // namespace srt +#include "atomic_clock.h" + #endif // INC_SRT_SYNC_H From 2243388a0ddd3296c4264b3187901849c00adeb8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 2 Sep 2021 13:07:35 +0200 Subject: [PATCH 257/790] [core] Removed ThreadName(const char*). (#2113) Now only string& can be passed to avoid checking for NULL. --- apps/srt-tunnel.cpp | 4 ++-- srtcore/api.cpp | 8 +++----- srtcore/core.cpp | 6 +++--- srtcore/sync.cpp | 4 ++-- srtcore/sync.h | 4 ++-- srtcore/threadname.h | 26 +++++++++++++------------- test/test_threadname.cpp | 4 ++-- testing/srt-test-multiplex.cpp | 2 +- testing/srt-test-relay.cpp | 2 +- testing/testactivemedia.hpp | 2 +- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index fd25fb998..fa08759a1 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -240,8 +240,8 @@ class Engine void Start() { Verb() << "START: " << media[DIR_IN]->uri() << " --> " << media[DIR_OUT]->uri(); - std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); - srt::ThreadName tn(thrn.c_str()); + const std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); + srt::ThreadName tn(thrn); thr = thread([this]() { Worker(); }); } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7f6d7ecaa..71386a566 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -154,9 +154,7 @@ bool srt::CUDTSocket::readReady() if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) return true; if (m_UDT.m_bListening) - { - return m_QueuedSockets.size() > 0; - } + return !m_QueuedSockets.empty(); return broken(); } @@ -955,7 +953,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) s->m_Status = SRTS_OPENED; // copy address information of local node - s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr(s->m_SelfAddr); return 0; } @@ -1535,7 +1533,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i HLOGC(aclog.Debug, log << "groupConnect: connecting a new socket with ISN=" << isn); connectIn(ns, target_addr, isn); } - catch (CUDTException& e) + catch (const CUDTException& e) { LOGC(aclog.Error, log << "groupConnect: socket @" << sid << " in group " << pg->id() << " failed to connect"); // We know it does belong to a group. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c887218d9..a5fe649d8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9395,10 +9395,10 @@ int srt::CUDT::processData(CUnit* in_unit) const string& tn = tns2.str(); - ThreadName tnkeep(tn.c_str()); - const char* thname = tn.c_str(); + ThreadName tnkeep(tn); + const string& thname = tn; #else - const char* thname = "SRT:TsbPd"; + const string thname = "SRT:TsbPd"; #endif if (!StartThread(m_RcvTsbPdThread, CUDT::tsbpd, this, thname)) return -1; diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 820e0a7f9..aa15c2471 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -79,9 +79,9 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) #ifdef ENABLE_STDCXX_SYNC -bool StartThread(CThread& th, ThreadFunc&& f, void* args, const char* name) +bool StartThread(CThread& th, ThreadFunc&& f, void* args, const string& name) #else -bool StartThread(CThread& th, void* (*f) (void*), void* args, const char* name) +bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name) #endif { ThreadName tn(name); diff --git a/srtcore/sync.h b/srtcore/sync.h index 00633da06..cd0288ad4 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -843,9 +843,9 @@ namespace this_thread /// #ifdef ENABLE_STDCXX_SYNC typedef void* (&ThreadFunc) (void*); -bool StartThread(CThread& th, ThreadFunc&& f, void* args, const char* name); +bool StartThread(CThread& th, ThreadFunc&& f, void* args, const std::string& name); #else -bool StartThread(CThread& th, void* (*f) (void*), void* args, const char* name); +bool StartThread(CThread& th, void* (*f) (void*), void* args, const std::string& name); #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/srtcore/threadname.h b/srtcore/threadname.h index a3dbb475e..3d60fbe4f 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -111,9 +111,9 @@ class ThreadName #endif } - ThreadNameImpl(const char* name) + explicit ThreadNameImpl(const char* name) + : reset(false) { - reset = false; tid = pthread_self(); if (!get(old_name)) @@ -140,6 +140,10 @@ class ThreadName if (tid == pthread_self()) set(old_name); } + + private: + ThreadNameImpl(ThreadNameImpl& other); + ThreadNameImpl& operator=(const ThreadNameImpl& other); private: bool reset; @@ -202,21 +206,17 @@ class ThreadName return ret; } - // note: set can fail if name is too long. The upper limit is platform - // dependent. strlen(name) <= 15 should work on most of the platform. - static bool set(const char* name) { return ThreadNameImpl::set(name); } - - static bool set(const std::string& name) { return set(name.c_str()); } - - ThreadName(const char* name) - : impl(name) - { - } + static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); } - ThreadName(const std::string& name) + explicit ThreadName(const std::string& name) : impl(name.c_str()) { } + +private: + ThreadName(const ThreadName&); + ThreadName(const char*); + ThreadName& operator=(const ThreadName& other); }; } // namespace srt diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp index 67301f97b..0b03686a4 100644 --- a/test/test_threadname.cpp +++ b/test/test_threadname.cpp @@ -28,12 +28,12 @@ TEST(ThreadName, GetSet) TEST(ThreadName, AutoReset) { - std::string old_name("old"); + const std::string old_name("old"); std::string new_name("new-name"); if (ThreadName::DUMMY_IMPL) { // just make sure the API is correct - ThreadName t("test"); + ThreadName t(std::string("test")); return; } diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index e2d5c8b89..df810aea6 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -196,7 +196,7 @@ class MediaBase med.name = name; // Ok, got this, so we can start transmission. - srt::ThreadName tn(thread_name.c_str()); + srt::ThreadName tn(thread_name); med.runner = thread( [&med]() { med.TransmissionLoop(); }); return med; diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 323718b51..0f72b8a1c 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -438,7 +438,7 @@ void SrtMainLoop::run() std::ostringstream tns; tns << "Input:" << this; - srt::ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str()); m_input_thr = thread([this] { try { InputRunner(); diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index fbb2f9dc8..c7b11e583 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -58,7 +58,7 @@ struct Medium running = true; std::ostringstream tns; tns << typeid(*this).name() << ":" << this; - srt::ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str()); thr = thread( [this] { RunnerBase(); } ); } From e8f4057590c0f5e7e05c7b8f76b763147f883bb7 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Fri, 3 Sep 2021 07:49:10 -0500 Subject: [PATCH 258/790] [build] Automatically link libatomic if needed. (#2116) --- CMakeLists.txt | 25 +++++- scripts/CheckCXXAtomic.cmake | 62 ++++++++++++++ scripts/CheckGCCAtomicIntrinsics.cmake | 110 +++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 scripts/CheckCXXAtomic.cmake create mode 100644 scripts/CheckGCCAtomicIntrinsics.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c5cfa1394..4e01e7e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -411,6 +411,20 @@ else() # Compiler altered by WITH_COMPILER_TYPE/PREFIX - can't rely on CMAKE_CXX message(STATUS "COMPILER CHANGED TO: ${COMPILER_TYPE} - forcing C++11 standard for apps") endif() +# Check for GCC Atomic Intrinsics and C++11 Atomics. +# Sets: +# HAVE_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC +include(CheckGCCAtomicIntrinsics) +CheckGCCAtomicIntrinsics() +# HAVE_CXX_ATOMIC +# HAVE_CXX_ATOMIC_STATIC +include(CheckCXXAtomic) +CheckCXXAtomic() + if (DISABLE_CXX11) set (ENABLE_CXX11 0) elseif( DEFINED ENABLE_CXX11 ) @@ -877,8 +891,6 @@ if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) - elseif (ANDROID) - target_link_libraries(${TARGET_srt}_shared PRIVATE atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_shared PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -913,8 +925,6 @@ if (srt_libspec_static) endif() elseif (MINGW) target_link_libraries(${TARGET_srt}_static PRIVATE wsock32 ws2_32) - elseif (ANDROID) - target_link_libraries(${TARGET_srt}_static PUBLIC atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_static PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -966,6 +976,13 @@ if (srt_libspec_shared) endif() endif() +# Required by some toolchains when statically linking this library if the +# GCC Atomic Intrinsics are being used. +if (HAVE_GCCATOMIC_INTRINSICS + AND HAVE_LIBATOMIC) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) +endif() + # Cygwin installs the *.dll libraries in bin directory and uses PATH. set (INSTALL_SHARED_DIR ${CMAKE_INSTALL_LIBDIR}) diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake new file mode 100644 index 000000000..e071b78a3 --- /dev/null +++ b/scripts/CheckCXXAtomic.cmake @@ -0,0 +1,62 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for c++11 std::atomic. +# +# Sets: +# HAVE_CXX_ATOMIC +# HAVE_CXX_ATOMIC_STATIC + +include(CheckCXXSourceCompiles) +include(CheckLibraryExists) + +function(CheckCXXAtomic) + + unset(HAVE_CXX_ATOMIC) + unset(HAVE_CXX_ATOMIC PARENT_SCOPE) + unset(HAVE_CXX_ATOMIC CACHE) + + unset(HAVE_CXX_ATOMIC_STATIC) + unset(HAVE_CXX_ATOMIC_STATIC PARENT_SCOPE) + unset(HAVE_CXX_ATOMIC_STATIC CACHE) + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckCXXAtomic_CODE + " + #include + #include + int main(void) + { + std::atomic x(0); + std::atomic y(0); + return x + y; + } + ") + + set(CMAKE_REQUIRED_FLAGS "-std=c++11") + + check_cxx_source_compiles( + "${CheckCXXAtomic_CODE}" + HAVE_CXX_ATOMIC) + + if(HAVE_CXX_ATOMIC) + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + check_cxx_source_compiles( + "${CheckCXXAtomic_CODE}" + HAVE_CXX_ATOMIC_STATIC) + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + +endfunction(CheckCXXAtomic) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake new file mode 100644 index 000000000..164fb190e --- /dev/null +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -0,0 +1,110 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for GCC Atomic Intrinsics and whether libatomic is required. +# +# Sets: +# HAVE_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC +# +# See +# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html +# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync + +include(CheckCSourceCompiles) +include(CheckLibraryExists) + +function(CheckGCCAtomicIntrinsics) + + unset(HAVE_LIBATOMIC) + unset(HAVE_LIBATOMIC PARENT_SCOPE) + unset(HAVE_LIBATOMIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS) + unset(HAVE_GCCATOMIC_INTRINSICS PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC CACHE) + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckGCCAtomicIntrinsics_CODE + " + #include + #include + int main(void) + { + ptrdiff_t x = 0; + intmax_t y = 0; + __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); + return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) + + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); + } + ") + + check_library_exists( + atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS) + + if (NOT HAVE_GCCATOMIC_INTRINSICS + AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + set(HAVE_GCCATOMIC_INTRINSICS TRUE PARENT_SCOPE) + endif() + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + if (HAVE_GCCATOMIC_INTRINSICS) + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_STATIC) + if (NOT HAVE_GCCATOMIC_INTRINSICS_STATIC + AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_STATIC) + if (HAVE_GCCATOMIC_INTRINSICS_STATIC) + set(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC TRUE PARENT_SCOPE) + endif() + endif() + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + +endfunction(CheckGCCAtomicIntrinsics) From 2e09a1696aba44e984e769eb2aa7c59a5905199a Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 6 Sep 2021 11:49:34 +0200 Subject: [PATCH 259/790] [build] Update Windows installer for alternate platform names (#2117) --- scripts/win-installer/libsrt.props | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props index d8de447c1..01e0b5dac 100644 --- a/scripts/win-installer/libsrt.props +++ b/scripts/win-installer/libsrt.props @@ -4,6 +4,25 @@ + + + + + Win32 + + + + + x64 + + + + + $(Platform) + + + + @@ -11,7 +30,7 @@ srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) - $(LIBSRT)\lib\$(Configuration)-$(Platform);%(AdditionalLibraryDirectories) + $(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories) /ignore:4099 %(AdditionalOptions) From e98146a2f2f75370fa7715ccf5f8a581cdb6ace8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 6 Sep 2021 14:37:40 +0200 Subject: [PATCH 260/790] [core] Avoid using strlen in ThreadName class --- srtcore/threadname.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 3d60fbe4f..4dade104a 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -111,7 +111,7 @@ class ThreadName #endif } - explicit ThreadNameImpl(const char* name) + explicit ThreadNameImpl(const std::string& name) : reset(false) { tid = pthread_self(); @@ -119,16 +119,16 @@ class ThreadName if (!get(old_name)) return; - reset = set(name); + reset = set(name.c_str()); if (reset) return; // Try with a shorter name. 15 is the upper limit supported by Linux, // other platforms should support a larger value. So 15 should works // on all platforms. - size_t max_len = 15; - if (std::strlen(name) > max_len) - reset = set(std::string(name, max_len).c_str()); + const size_t max_len = 15; + if (name.size() > max_len) + reset = set(name.substr(0, max_len).c_str()); } ~ThreadNameImpl() @@ -171,7 +171,7 @@ class ThreadName static bool set(const char*) { return false; } - ThreadNameImpl(const char*) {} + ThreadNameImpl(const std::string&) {} ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version { @@ -209,7 +209,7 @@ class ThreadName static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); } explicit ThreadName(const std::string& name) - : impl(name.c_str()) + : impl(name) { } From 34ba95157a0c27c85ee20450d4b481cec6c99f81 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Tue, 7 Sep 2021 12:43:28 +0200 Subject: [PATCH 261/790] Improved Access Control general syntax description --- docs/features/access-control.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/features/access-control.md b/docs/features/access-control.md index d583f795b..e6f9124c8 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -41,23 +41,23 @@ The Stream ID uses UTF-8 encoding. ## General Syntax -This recommended syntax starts with the characters known as an executable -specification in POSIX: `#!`. +The recommended syntax starts with the characters known as an executable specification in POSIX: `#!`. -The next two characters are: +The next character defines the format used for the following key-value pair syntax. +At the moment, there is only one supported syntax identified by `:` and described below. -- `:` - marks the format of the following key-value pair syntax (the only one defined currently). -- The content format, which is either: - - `:` - the comma-separated keys with no nesting - - `{` - like above, but nesting is allowed and must end with `}` +Everything that comes after a syntax identifier is further referenced as the content of the Stream ID. -(Nesting means that you can have multiple level brace-enclosed parts inside.) +The content starts with a `:` or `{` character identifying its format: -The form of the key-value pair is: +- `:` : comma-separated key-value pairs with no nesting, +- `{` : a nested block with one or several key-value pairs that must end with a `}` character. Nesting means that multiple level brace-enclosed parts are allowed. -```js -key1=value1,key2=value2... -``` +The form of the key-value pair is + +~~~ +key1=value1,key2=value2,... +~~~ ## Standard Keys From b147d5ec9ab7134b3eb8b5ef2b11039cf8bdd883 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 8 Sep 2021 04:28:27 -0500 Subject: [PATCH 262/790] [core] Fix Solaris build. (#2115) --- CMakeLists.txt | 11 +++++++--- apps/srt-tunnel.cpp | 22 ++++++++++---------- apps/transmitmedia.cpp | 5 ++++- srtcore/common.cpp | 2 +- srtcore/epoll.cpp | 3 ++- srtcore/list.cpp | 8 ++++---- srtcore/srt_compat.c | 2 +- srtcore/sync.h | 22 ++++++++++---------- srtcore/sync_cxx11.cpp | 4 ++-- srtcore/utilities.h | 40 +++++++++++++++++++++++++++++++++++++ testing/testactivemedia.cpp | 6 +++--- testing/testactivemedia.hpp | 6 +++--- 12 files changed, 90 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e01e7e63..3cb8d82a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,9 @@ set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") set_if(ANDROID ${SYSNAME_LC} MATCHES "android") -set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) -set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) +set_if(SUNOS "${SYSNAME_LC}" MATCHES "sunos") +set_if(POSIX LINUX OR DARWIN OR BSD OR SUNOS OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) +set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR SUNOS OR CYGWIN OR GNU) # Not sure what to do in case of compiling by MSVC. # This will make installdir in C:\Program Files\SRT then @@ -282,8 +283,9 @@ if (DEFINED HAVE_INET_PTON) add_definitions(-DHAVE_INET_PTON=1) endif() +# Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* include(FindPThreadGetSetName) -FindPThreadGetSetName() # Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* +FindPThreadGetSetName() if (ENABLE_MONOTONIC_CLOCK) if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) @@ -617,6 +619,9 @@ elseif(CYGWIN) elseif(GNU) add_definitions(-DGNU=1) message(STATUS "DETECTED SYSTEM: GNU; GNU=1" ) +elseif(SUNOS) + add_definitions(-DSUNOS=1) + message(STATUS "DETECTED SYSTEM: SunOS|Solaris; SUNOS=1" ) else() message(FATAL_ERROR "Unsupported system: ${CMAKE_SYSTEM_NAME}") endif() diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index fa08759a1..ebc996c9a 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -94,7 +94,7 @@ class Medium bool m_eof = false; bool m_broken = false; - mutex access; // For closing + std::mutex access; // For closing template static Medium* CreateAcceptor(DerivedMedium* self, const sockaddr_any& sa, SocketType sock, size_t chunk) @@ -287,7 +287,7 @@ class Tunnel std::unique_ptr med_acp, med_clr; Engine acp_to_clr, clr_to_acp; volatile bool running = true; - mutex access; + std::mutex access; public: @@ -324,7 +324,7 @@ class Tunnel /* { - lock_guard lk(access); + lock_guard lk(access); if (acp_to_clr.stat() == -1 && clr_to_acp.stat() == -1) { Verb() << "Tunnel: Both engine decommissioned, will stop the tunnel."; @@ -438,7 +438,7 @@ class SrtMedium: public Medium void CloseSrt() { Verb() << "Closing SRT socket for " << uri(); - lock_guard lk(access); + lock_guard lk(access); if (m_socket == SRT_ERROR) return; srt_close(m_socket); @@ -528,7 +528,7 @@ class TcpMedium: public Medium void CloseTcp() { Verb() << "Closing TCP socket for " << uri(); - lock_guard lk(access); + lock_guard lk(access); if (m_socket == -1) return; tcp_close(m_socket); @@ -954,20 +954,20 @@ std::unique_ptr Medium::Create(const std::string& url, size_t chunk, Med struct Tunnelbox { list> tunnels; - mutex access; + std::mutex access; condition_variable decom_ready; bool main_running = true; thread thr; void signal_decommission() { - lock_guard lk(access); + lock_guard lk(access); decom_ready.notify_one(); } void install(std::unique_ptr&& acp, std::unique_ptr&& clr) { - lock_guard lk(access); + lock_guard lk(access); Verb() << "Tunnelbox: Starting tunnel: " << acp->uri() << " <-> " << clr->uri(); tunnels.emplace_back(new Tunnel(this, move(acp), move(clr))); @@ -992,7 +992,7 @@ struct Tunnelbox void CleanupWorker() { - unique_lock lk(access); + unique_lock lk(access); while (main_running) { @@ -1027,7 +1027,7 @@ void Tunnel::Stop() if (!running) return; // already stopped - lock_guard lk(access); + lock_guard lk(access); // Ok, you are the first to make the tunnel // not running and inform the tunnelbox. @@ -1037,7 +1037,7 @@ void Tunnel::Stop() bool Tunnel::decommission_if_dead(bool forced) { - lock_guard lk(access); + lock_guard lk(access); if (running && !forced) return false; // working, not to be decommissioned diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index e5f76206d..c61c703b8 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -22,9 +22,12 @@ #if !defined(_WIN32) #include #else -#include +#include #include #endif +#if defined(SUNOS) +#include +#endif #include "netinet_any.h" #include "apputil.hpp" diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 0e19dc611..cd151116a 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -98,7 +98,7 @@ const char* CUDTException::getErrorMessage() const ATR_NOTHROW return srt::strerror_get_message(m_iMajor, m_iMinor); } -string CUDTException::getErrorString() const +std::string CUDTException::getErrorString() const { return getErrorMessage(); } diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index d4919ff39..20a9c0bbb 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -149,7 +149,8 @@ ENOMEM: There was insufficient memory to create the kernel object. if (localid < 0) throw CUDTException(MJ_SETUP, MN_NONE, errno); #else - // on Solaris, use /dev/poll + // TODO: Solaris, use port_getn() + // https://docs.oracle.com/cd/E86824_01/html/E54766/port-get-3c.html // on Windows, select #endif diff --git a/srtcore/list.cpp b/srtcore/list.cpp index c7ab7cfc8..e295fb8c8 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -100,13 +100,13 @@ void CSndLossList::traceState() const int pos = m_iHead; while (pos != SRT_SEQNO_NONE) { - ::cout << pos << ":[" << m_caSeq[pos].seqstart; + std::cout << pos << ":[" << m_caSeq[pos].seqstart; if (m_caSeq[pos].seqend != SRT_SEQNO_NONE) - ::cout << ", " << m_caSeq[pos].seqend; - ::cout << "], "; + std::cout << ", " << m_caSeq[pos].seqend; + std::cout << "], "; pos = m_caSeq[pos].inext; } - ::cout << "\n"; + std::cout << "\n"; } int CSndLossList::insert(int32_t seqno1, int32_t seqno2) diff --git a/srtcore/srt_compat.c b/srtcore/srt_compat.c index 32c8dd6fb..1473a7b94 100644 --- a/srtcore/srt_compat.c +++ b/srtcore/srt_compat.c @@ -23,7 +23,7 @@ written by #include #include #include -#if defined(__unix__) && !defined(BSD) +#if defined(__unix__) && !defined(BSD) && !defined(SUNOS) #include #endif diff --git a/srtcore/sync.h b/srtcore/sync.h index cd0288ad4..7cb0f63a9 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -60,7 +60,6 @@ namespace srt { namespace sync { -using namespace std; /////////////////////////////////////////////////////////////////////////////// // @@ -71,7 +70,7 @@ using namespace std; #if ENABLE_STDCXX_SYNC template -using Duration = chrono::duration; +using Duration = std::chrono::duration; #else @@ -130,13 +129,13 @@ class Duration #if ENABLE_STDCXX_SYNC -using steady_clock = chrono::steady_clock; +using steady_clock = std::chrono::steady_clock; template -using time_point = chrono::time_point; +using time_point = std::chrono::time_point; template -using TimePoint = chrono::time_point; +using TimePoint = std::chrono::time_point; template inline bool is_zero(const time_point &tp) @@ -212,8 +211,8 @@ class TimePoint inline void operator-=(const Duration& rhs) { m_timestamp -= rhs.count(); } public: // - static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(numeric_limits::min()); } - static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(numeric_limits::max()); } + static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(std::numeric_limits::min()); } + static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(std::numeric_limits::max()); } public: Duration time_since_epoch() const; @@ -311,9 +310,9 @@ inline bool is_zero(const TimePoint& t) /////////////////////////////////////////////////////////////////////////////// #if ENABLE_STDCXX_SYNC -using Mutex = mutex; -using UniqueLock = unique_lock; -using ScopedLock = lock_guard; +using Mutex = std::mutex; +using UniqueLock = std::unique_lock; +using ScopedLock = std::lock_guard; #else /// Mutex is a class wrapper, that should mimic the std::chrono::mutex class. /// At the moment the extra function ref() is temporally added to allow calls @@ -471,7 +470,7 @@ class Condition private: #if ENABLE_STDCXX_SYNC - condition_variable m_cv; + std::condition_variable m_cv; #else pthread_cond_t m_cv; #endif @@ -742,6 +741,7 @@ class CGlobEvent #ifdef ENABLE_STDCXX_SYNC typedef std::system_error CThreadException; using CThread = std::thread; +namespace this_thread = std::this_thread; #else // pthreads wrapper version typedef ::CUDTException CThreadException; diff --git a/srtcore/sync_cxx11.cpp b/srtcore/sync_cxx11.cpp index 56a63a4bd..4dae3563e 100644 --- a/srtcore/sync_cxx11.cpp +++ b/srtcore/sync_cxx11.cpp @@ -68,12 +68,12 @@ void srt::sync::Condition::wait(UniqueLock& lock) bool srt::sync::Condition::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time) { // Another possible implementation is wait_until(steady_clock::now() + timeout); - return m_cv.wait_for(lock, rel_time) != cv_status::timeout; + return m_cv.wait_for(lock, rel_time) != std::cv_status::timeout; } bool srt::sync::Condition::wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time) { - return m_cv.wait_until(lock, timeout_time) != cv_status::timeout; + return m_cv.wait_until(lock, timeout_time) != std::cv_status::timeout; } void srt::sync::Condition::notify_one() diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 60a286ec0..f787a579e 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -140,6 +140,46 @@ written by # define le64toh(x) letoh64(x) #endif +#elif defined(SUNOS) + + // SunOS/Solaris + + #include + #include + + #define __LITTLE_ENDIAN 1234 + #define __BIG_ENDIAN 4321 + + # if defined(_BIG_ENDIAN) + #define __BYTE_ORDER __BIG_ENDIAN + #define be64toh(x) (x) + #define be32toh(x) (x) + #define be16toh(x) (x) + #define le16toh(x) ((uint16_t)BSWAP_16(x)) + #define le32toh(x) BSWAP_32(x) + #define le64toh(x) BSWAP_64(x) + #define htobe16(x) (x) + #define htole16(x) ((uint16_t)BSWAP_16(x)) + #define htobe32(x) (x) + #define htole32(x) BSWAP_32(x) + #define htobe64(x) (x) + #define htole64(x) BSWAP_64(x) + # else + #define __BYTE_ORDER __LITTLE_ENDIAN + #define be64toh(x) BSWAP_64(x) + #define be32toh(x) ntohl(x) + #define be16toh(x) ntohs(x) + #define le16toh(x) (x) + #define le32toh(x) (x) + #define le64toh(x) (x) + #define htobe16(x) htons(x) + #define htole16(x) (x) + #define htobe32(x) htonl(x) + #define htole32(x) (x) + #define htobe64(x) BSWAP_64(x) + #define htole64(x) (x) + # endif + #elif defined(__WINDOWS__) # include diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 53f42a9a1..7d062f49d 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -16,7 +16,7 @@ void SourceMedium::Runner() } LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", input.payload.size(), "] MEDIUM -> BUFFER. signal(", &ready, ")"); - lock_guard g(buffer_lock); + lock_guard g(buffer_lock); buffer.push_back(input); ready.notify_one(); } @@ -24,7 +24,7 @@ void SourceMedium::Runner() MediaPacket SourceMedium::Extract() { - unique_lock g(buffer_lock); + unique_lock g(buffer_lock); for (;;) { if (::transmit_int_state) @@ -70,7 +70,7 @@ void TargetMedium::Runner() { MediaPacket val; { - unique_lock lg(buffer_lock); + unique_lock lg(buffer_lock); if (buffer.empty()) { if (!running) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index c7b11e583..f4bc360ba 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -150,7 +150,7 @@ struct TargetMedium: Medium bool Schedule(const MediaPacket& data) { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); - lock_guard lg(buffer_lock); + 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); + lock_guard lg(buffer_lock); buffer.clear(); } void Interrupt() { - lock_guard lg(buffer_lock); + lock_guard lg(buffer_lock); running = false; ready.notify_one(); } From ad84c38e40ba36e18699dd5998684540620697a1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 14 Sep 2021 12:11:22 +0200 Subject: [PATCH 263/790] [core] Fixed FileCC random decrease --- srtcore/congctl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 2301dfc68..b394f8183 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -535,8 +535,7 @@ class FileCC : public SrtCongestionControlBase m_iLastDecSeq = m_parent->sndSeqNo(); - // remove global synchronization using randomization. - m_iDecRandom = genRandomInt(1, m_iAvgNAKNum); + m_iDecRandom = m_iAvgNAKNum > 1 ? genRandomInt(1, m_iAvgNAKNum) : 1; SRT_ASSERT(m_iDecRandom >= 1); HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin << ", lastsentseqno=" << m_iLastDecSeq From 4d0fe6166768d524d28e35e35c72bc12de0d46b5 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Thu, 16 Sep 2021 05:00:15 -0500 Subject: [PATCH 264/790] [build] Fixes build with CMake <3.0.2. (#2123) --- scripts/CheckCXXAtomic.cmake | 10 +--- scripts/CheckGCCAtomicIntrinsics.cmake | 25 ++------ scripts/FindPThreadGetSetName.cmake | 80 ++++++++++++-------------- scripts/UnSetVariableFull.cmake | 27 +++++++++ 4 files changed, 72 insertions(+), 70 deletions(-) create mode 100644 scripts/UnSetVariableFull.cmake diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake index e071b78a3..8c2d141a1 100644 --- a/scripts/CheckCXXAtomic.cmake +++ b/scripts/CheckCXXAtomic.cmake @@ -15,16 +15,12 @@ include(CheckCXXSourceCompiles) include(CheckLibraryExists) +include(UnSetVariableFull) function(CheckCXXAtomic) - unset(HAVE_CXX_ATOMIC) - unset(HAVE_CXX_ATOMIC PARENT_SCOPE) - unset(HAVE_CXX_ATOMIC CACHE) - - unset(HAVE_CXX_ATOMIC_STATIC) - unset(HAVE_CXX_ATOMIC_STATIC PARENT_SCOPE) - unset(HAVE_CXX_ATOMIC_STATIC CACHE) + UnSetVariableFull(HAVE_CXX_ATOMIC) + UnSetVariableFull(HAVE_CXX_ATOMIC_STATIC) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 164fb190e..8af1bd091 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -22,28 +22,15 @@ include(CheckCSourceCompiles) include(CheckLibraryExists) +include(UnSetVariableFull) function(CheckGCCAtomicIntrinsics) - unset(HAVE_LIBATOMIC) - unset(HAVE_LIBATOMIC PARENT_SCOPE) - unset(HAVE_LIBATOMIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS) - unset(HAVE_GCCATOMIC_INTRINSICS PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC CACHE) + UnSetVariableFull(HAVE_LIBATOMIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake index fa818fd73..c78bca2be 100644 --- a/scripts/FindPThreadGetSetName.cmake +++ b/scripts/FindPThreadGetSetName.cmake @@ -27,56 +27,48 @@ # add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) include(CheckSymbolExists) +include(UnSetVariableFull) function(FindPThreadGetSetName) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP) + UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP) - unset(HAVE_PTHREAD_GETNAME_NP) - unset(HAVE_PTHREAD_GETNAME_NP PARENT_SCOPE) - unset(HAVE_PTHREAD_GETNAME_NP CACHE) - unset(HAVE_PTHREAD_SETNAME_NP) - unset(HAVE_PTHREAD_SETNAME_NP PARENT_SCOPE) - unset(HAVE_PTHREAD_SETNAME_NP CACHE) + set(CMAKE_REQUIRED_DEFINITIONS + -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) + set(CMAKE_REQUIRED_FLAGS "-pthread") - set(CMAKE_REQUIRED_DEFINITIONS - -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) - set(CMAKE_REQUIRED_FLAGS "-pthread") + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") + check_symbol_exists( + pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + check_symbol_exists( + pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) + endif() - message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") - check_symbol_exists( - pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) - endif() - check_symbol_exists( - pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) - endif() + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") + check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + endif() + check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + endif() + if (HAVE_PTHREAD_GETNAME_NP) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) + endif() + if (HAVE_PTHREAD_SETNAME_NP) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + endif() - message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") - check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) - if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) - endif() - check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) - if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) - endif() - if (HAVE_PTHREAD_GETNAME_NP) - add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) - endif() - if (HAVE_PTHREAD_SETNAME_NP) - add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) - endif() - - unset(CMAKE_REQUIRED_DEFINITIONS) - unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_FLAGS) endfunction(FindPThreadGetSetName) diff --git a/scripts/UnSetVariableFull.cmake b/scripts/UnSetVariableFull.cmake new file mode 100644 index 000000000..f751374ee --- /dev/null +++ b/scripts/UnSetVariableFull.cmake @@ -0,0 +1,27 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Notes: +# +# Macro that UnSets a variable in Cache, Local Scope, and Parent Scope. +# +# Usage: +# +# UnSetVariableFull() + +macro(UnSetVariableFull tVariable) + unset(tVariable) + unset(tVariable CACHE) + # unset(.... PARENT_SCOPE) was introduced in cmake-3.0.2. + if ("${CMAKE_VERSION}" VERSION_LESS "3.0.2") + set(tVariable "" PARENT_SCOPE) + else() + unset(tVariable PARENT_SCOPE) + endif() +endmacro() From f9a54a033bf0ece3071f0fb297eb1130b8d9de7d Mon Sep 17 00:00:00 2001 From: Yanko Kaneti Date: Sat, 18 Sep 2021 21:53:44 +0300 Subject: [PATCH 265/790] [build] Build but do not install test-srt --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cb8d82a6..d1e7929e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1062,10 +1062,14 @@ else() message(FATAL_ERROR "Either ENABLE_STATIC or ENABLE_SHARED has to be ON!") endif() -macro(srt_add_program name) +macro(srt_add_program_dont_install name) add_executable(${name} ${ARGN}) target_include_directories(${name} PRIVATE apps) target_include_directories(${name} PRIVATE common) +endmacro() + +macro(srt_add_program name) + srt_add_program_dont_install(${name} ${ARGN}) install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endmacro() @@ -1276,7 +1280,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) SOURCES SOURCES_unittests ) - srt_add_program(test-srt ${SOURCES_unittests}) + srt_add_program_dont_install(test-srt ${SOURCES_unittests}) srt_make_application(test-srt) target_include_directories(test-srt PRIVATE ${SSL_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) From add40584757636ec99eef41ea3ea4687a43feb3f Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 20 Sep 2021 16:45:53 +0200 Subject: [PATCH 266/790] [core] Fixed 'undef' warning with gcc and clang (#2131) --- srtcore/platform_sys.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index 79760080e..cb5d0afd9 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -45,7 +45,15 @@ #endif #else -#if __APPLE__ +#if defined(__APPLE__) && __APPLE__ +// Warning: please keep this test as it is, do not make it +// "#if __APPLE__" or "#ifdef __APPLE__". In applications with +// a strict "no warning policy", "#if __APPLE__" triggers an "undef" +// error. With GCC, an old & never fixed bug prevents muting this +// warning (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431). +// Before this fix, the solution was to "#define __APPLE__ 0" before +// including srt.h. So, don't use "#ifdef __APPLE__" either. + // XXX Check if this condition doesn't require checking of // also other macros, like TARGET_OS_IOS etc. From 1d862c43c87678b2f02415ce958dd822786572a2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 22 Sep 2021 17:14:41 +0200 Subject: [PATCH 267/790] [build] Fixed a typo 'availabe' --- scripts/haiUtil.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/haiUtil.cmake b/scripts/haiUtil.cmake index 9e4fb4d56..222661989 100644 --- a/scripts/haiUtil.cmake +++ b/scripts/haiUtil.cmake @@ -252,7 +252,7 @@ function (test_requires_clock_gettime _enable _linklib) check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN) if (HAVE_CLOCK_GETTIME_IN) - message(STATUS "CLOCK_MONOTONIC: availabe, no extra libs needed") + message(STATUS "CLOCK_MONOTONIC: available, no extra libs needed") set (${_enable} ON PARENT_SCOPE) set (${_linklib} "" PARENT_SCOPE) return() From 224042a5867b11a06ddb5fb0f40d93ffb2b22494 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Wed, 22 Sep 2021 11:38:14 +0200 Subject: [PATCH 268/790] [docs] Updated SRTO_RETRANSMITALGO socket option description --- docs/API/API-socket-options.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 84606135a..7f85d6800 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1277,17 +1277,20 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | | `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | -Retransmission algorithm to use (SENDER option): +An SRT sender option to choose between two retransmission algorithms: -- 0 - Retransmit on every loss report (higher overhead, but slightly higher chance to recover a lost packet). -- 1 - Reduced retransmissions (retransmit not more often than once per RTT); reduced - bandwidth consumption. +- 0 - an intensive retransmission algorithm (default until SRT v1.4.4), and +- 1 - a new efficient retransmission algorithm (introduced in SRT v1.4.2; default since SRT v1.4.4). -This option is effective only on the sending side. It influences the decision +The intensive retransmission algorithm causes the SRT sender to schedule a packet for retransmission each time it receives a negative acknowledgement (NAK). On a network characterized by low packet loss levels and link capacity high enough to accommodate extra retransmission overhead, this algorithm increases the chances of recovering from packet loss with a minimum delay, and may better suit end-to-end latency constraints. + +The new efficient algorithm optimizes the bandwidth usage by producing fewer retransmissions per lost packet. It takes SRT statistics into account to determine if a retransmitted packet is still in flight and could reach the receiver in time, so that some of the NAK reports are ignored by the sender. This algorithm better fits general use cases, as well as cases where channel bandwidth is limited. + +NOTE: This option is effective only on the sending side. It influences the decision as to whether a particular reported lost packet should be retransmitted at a certain time or not. -The reduced retransmission algorithm (`SRTO_RETRANSMITALGO=1`) is only operational when receiver sends +NOTE: The efficient retransmission algorithm can only be used when a receiver sends Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). [Return to list](#list-of-options) From d8127a813d21909149fa94690845ec95d17977c8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 23 Sep 2021 11:54:00 +0200 Subject: [PATCH 269/790] [build] Fixed CMake warning about string --- scripts/iOS.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/iOS.cmake b/scripts/iOS.cmake index 73544a29b..dfb6aeb97 100644 --- a/scripts/iOS.cmake +++ b/scripts/iOS.cmake @@ -151,10 +151,10 @@ if (NOT DEFINED IOS_ARCH) set (IOS_ARCH x86_64) endif (${IOS_PLATFORM} STREQUAL OS) endif(NOT DEFINED IOS_ARCH) -set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") +set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS") # Set the find root to the iOS developer roots and to user defined paths -set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") +set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root") # default to searching for frameworks first set (CMAKE_FIND_FRAMEWORK FIRST) From 60891f31bb3f167497237e963b142fa777eecb50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 28 Sep 2021 14:53:02 +0200 Subject: [PATCH 270/790] [core] Fixed 'atomic' linking for iOS target (#2137) Co-authored-by: Jose Santiago --- CMakeLists.txt | 20 +++++++------ scripts/CheckCXXAtomic.cmake | 12 +++++--- scripts/CheckGCCAtomicIntrinsics.cmake | 40 ++++---------------------- scripts/FindPThreadGetSetName.cmake | 13 ++++----- scripts/UnSetVariableFull.cmake | 27 ----------------- 5 files changed, 30 insertions(+), 82 deletions(-) delete mode 100644 scripts/UnSetVariableFull.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d1e7929e1..7200491df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -418,8 +418,6 @@ endif() # HAVE_LIBATOMIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC include(CheckGCCAtomicIntrinsics) CheckGCCAtomicIntrinsics() # HAVE_CXX_ATOMIC @@ -887,7 +885,7 @@ if (srt_libspec_shared) endif() if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PRIVATE ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_shared PRIVATE crypt32.lib) else() set_target_properties(${TARGET_srt}_shared PROPERTIES LINK_FLAGS "/DELAYLOAD:libeay32.dll") @@ -925,7 +923,7 @@ if (srt_libspec_static) endif() if (MICROSOFT) target_link_libraries(${TARGET_srt}_static PRIVATE ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_static PRIVATE crypt32.lib) endif() elseif (MINGW) @@ -938,8 +936,8 @@ endif() target_include_directories(srt_virtual PRIVATE ${SSL_INCLUDE_DIRS}) -if (MICROSOFT) - if (OPENSSL_USE_STATIC_LIBS) +if (MICROSOFT) + if (OPENSSL_USE_STATIC_LIBS) set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ws2_32.lib crypt32.lib) else() set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ws2_32.lib) @@ -983,9 +981,13 @@ endif() # Required by some toolchains when statically linking this library if the # GCC Atomic Intrinsics are being used. -if (HAVE_GCCATOMIC_INTRINSICS - AND HAVE_LIBATOMIC) - target_link_libraries(${TARGET_srt}_static PUBLIC atomic) +if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC AND HAVE_LIBATOMIC) + if (srt_libspec_static) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) + endif() + if (srt_libspec_shared) + target_link_libraries(${TARGET_srt}_shared PUBLIC atomic) + endif() endif() # Cygwin installs the *.dll libraries in bin directory and uses PATH. diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake index 8c2d141a1..d3a3b9e71 100644 --- a/scripts/CheckCXXAtomic.cmake +++ b/scripts/CheckCXXAtomic.cmake @@ -15,12 +15,11 @@ include(CheckCXXSourceCompiles) include(CheckLibraryExists) -include(UnSetVariableFull) function(CheckCXXAtomic) - UnSetVariableFull(HAVE_CXX_ATOMIC) - UnSetVariableFull(HAVE_CXX_ATOMIC_STATIC) + unset(HAVE_CXX_ATOMIC CACHE) + unset(HAVE_CXX_ATOMIC_STATIC CACHE) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) @@ -45,7 +44,12 @@ function(CheckCXXAtomic) HAVE_CXX_ATOMIC) if(HAVE_CXX_ATOMIC) - set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + # CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14. + if(CMAKE_VERSION VERSION_LESS "3.14") + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + else() + set(CMAKE_REQUIRED_FLAGS "-std=c++11 -static") + endif() check_cxx_source_compiles( "${CheckCXXAtomic_CODE}" HAVE_CXX_ATOMIC_STATIC) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 8af1bd091..9429db12f 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -13,8 +13,6 @@ # HAVE_LIBATOMIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC # # See # https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html @@ -22,15 +20,12 @@ include(CheckCSourceCompiles) include(CheckLibraryExists) -include(UnSetVariableFull) function(CheckGCCAtomicIntrinsics) - UnSetVariableFull(HAVE_LIBATOMIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) + unset(HAVE_LIBATOMIC CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) @@ -54,12 +49,12 @@ function(CheckGCCAtomicIntrinsics) check_library_exists( atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 check_c_source_compiles( "${CheckGCCAtomicIntrinsics_CODE}" HAVE_GCCATOMIC_INTRINSICS) - if (NOT HAVE_GCCATOMIC_INTRINSICS - AND HAVE_LIBATOMIC) + if (NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) set(CMAKE_REQUIRED_LIBRARIES "atomic") check_c_source_compiles( "${CheckGCCAtomicIntrinsics_CODE}" @@ -69,29 +64,4 @@ function(CheckGCCAtomicIntrinsics) endif() endif() - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) - - if (HAVE_GCCATOMIC_INTRINSICS) - set(CMAKE_REQUIRED_LINK_OPTIONS "-static") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_STATIC) - if (NOT HAVE_GCCATOMIC_INTRINSICS_STATIC - AND HAVE_LIBATOMIC) - set(CMAKE_REQUIRED_LIBRARIES "atomic") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_STATIC) - if (HAVE_GCCATOMIC_INTRINSICS_STATIC) - set(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC TRUE PARENT_SCOPE) - endif() - endif() - endif() - - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) - endfunction(CheckGCCAtomicIntrinsics) diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake index c78bca2be..65685e1eb 100644 --- a/scripts/FindPThreadGetSetName.cmake +++ b/scripts/FindPThreadGetSetName.cmake @@ -27,14 +27,13 @@ # add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) include(CheckSymbolExists) -include(UnSetVariableFull) function(FindPThreadGetSetName) - UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP) - UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_GETNAME_NP CACHE) + unset(HAVE_PTHREAD_SETNAME_NP CACHE) set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) @@ -55,11 +54,11 @@ function(FindPThreadGetSetName) message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + set(HAVE_PTHREAD_GETNAME_NP 1 CACHE INTERNAL "" FORCE) endif() check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + set(HAVE_PTHREAD_SETNAME_NP 1 CACHE INTERNAL "" FORCE) endif() if (HAVE_PTHREAD_GETNAME_NP) add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) diff --git a/scripts/UnSetVariableFull.cmake b/scripts/UnSetVariableFull.cmake deleted file mode 100644 index f751374ee..000000000 --- a/scripts/UnSetVariableFull.cmake +++ /dev/null @@ -1,27 +0,0 @@ -# -# SRT - Secure, Reliable, Transport -# Copyright (c) 2021 Haivision Systems Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# - -# Notes: -# -# Macro that UnSets a variable in Cache, Local Scope, and Parent Scope. -# -# Usage: -# -# UnSetVariableFull() - -macro(UnSetVariableFull tVariable) - unset(tVariable) - unset(tVariable CACHE) - # unset(.... PARENT_SCOPE) was introduced in cmake-3.0.2. - if ("${CMAKE_VERSION}" VERSION_LESS "3.0.2") - set(tVariable "" PARENT_SCOPE) - else() - unset(tVariable PARENT_SCOPE) - endif() -endmacro() From 6fda50220aa848145fb97d5382e66aa59cb81bff Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 28 Sep 2021 15:15:28 +0200 Subject: [PATCH 271/790] [build] Added iOS build to GitHub Actions CI (#2136) --- .github/workflows/iOS.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/iOS.yaml diff --git a/.github/workflows/iOS.yaml b/.github/workflows/iOS.yaml new file mode 100644 index 000000000..60d1e8d3a --- /dev/null +++ b/.github/workflows/iOS.yaml @@ -0,0 +1,25 @@ +name: iOS + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + strategy: + matrix: + cxxstdsync: [OFF, ON] + + name: iOS-cxxsync${{ matrix.cxxstdsync }} + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: configure + run: | + mkdir _build && cd _build + cmake ../ -DENABLE_ENCRYPTION=OFF -DENABLE_STDCXX_SYNC=${{matrix.cxxstdsync}} -DENABLE_UNITTESTS=OFF -DENABLE_EXPERIMENTAL_BONDING=ON --toolchain scripts/iOS.cmake + - name: build + run: cd _build && cmake --build ./ From cdec1145c8430a66b535636a14139531e51d9cb1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Tue, 28 Sep 2021 16:45:36 +0200 Subject: [PATCH 272/790] [build] Fix build Android with NDK <17. Need to link against libatomic when compiling statically --- CMakeLists.txt | 10 +- scripts/CheckGCCAtomicIntrinsics.cmake | 136 +++++++++++++++++-------- 2 files changed, 100 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7200491df..21478c36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,6 +416,8 @@ endif() # Check for GCC Atomic Intrinsics and C++11 Atomics. # Sets: # HAVE_LIBATOMIC +# HAVE_LIBATOMIC_COMPILES +# HAVE_LIBATOMIC_COMPILES_STATIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC include(CheckGCCAtomicIntrinsics) @@ -973,7 +975,7 @@ endif() if (srt_libspec_shared) if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PUBLIC Ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_shared PUBLIC crypt32.lib) endif() endif() @@ -988,6 +990,12 @@ if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC AND HAVE_LIBATOMIC) if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PUBLIC atomic) endif() +elseif (HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES_STATIC) + # This is a workaround for ANDROID NDK<17 builds, which need to link + # to libatomic when linking statically to the SRT library. + if (srt_libspec_static) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) + endif() endif() # Cygwin installs the *.dll libraries in bin directory and uses PATH. diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 9429db12f..f47b14d61 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -1,67 +1,113 @@ # -# SRT - Secure, Reliable, Transport -# Copyright (c) 2021 Haivision Systems Inc. +# SRT - Secure, Reliable, Transport Copyright (c) 2021 Haivision Systems Inc. # -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at http://mozilla.org/MPL/2.0/. # # Check for GCC Atomic Intrinsics and whether libatomic is required. # # Sets: -# HAVE_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS -# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_LIBATOMIC +# HAVE_LIBATOMIC_COMPILES +# HAVE_LIBATOMIC_COMPILES_STATIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC # # See -# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html -# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync +# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html +# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync include(CheckCSourceCompiles) include(CheckLibraryExists) function(CheckGCCAtomicIntrinsics) - unset(HAVE_LIBATOMIC CACHE) - unset(HAVE_GCCATOMIC_INTRINSICS CACHE) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) + unset(HAVE_LIBATOMIC CACHE) + unset(HAVE_LIBATOMIC_COMPILES CACHE) + unset(HAVE_LIBATOMIC_COMPILES_STATIC CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 - set(CheckGCCAtomicIntrinsics_CODE - " - #include - #include - int main(void) - { - ptrdiff_t x = 0; - intmax_t y = 0; - __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); - __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); - return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) - + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); - } - ") + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) - check_library_exists( - atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + # Check for existance of libatomic and whether this symbol is present. + check_library_exists(atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) - set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS) + set(CheckLibAtomicCompiles_CODE + " + int main(void) + { + const int result = 0; + return result; + } + ") - if (NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) - set(CMAKE_REQUIRED_LIBRARIES "atomic") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - set(HAVE_GCCATOMIC_INTRINSICS TRUE PARENT_SCOPE) - endif() - endif() + set(CMAKE_REQUIRED_LIBRARIES "atomic") + + # Check that the compiler can build a simple application and link with + # libatomic. + check_c_source_compiles("${CheckLibAtomicCompiles_CODE}" + HAVE_LIBATOMIC_COMPILES) + if(NOT HAVE_LIBATOMIC_COMPILES) + set(HAVE_LIBATOMIC + 0 + CACHE INTERNAL "" FORCE) + endif() + if(HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES) + # CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14. + if(CMAKE_VERSION VERSION_LESS "3.14") + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + else() + set(CMAKE_REQUIRED_FLAGS "-static") + endif() + # Check that the compiler can build a simple application and statically link + # with libatomic. + check_c_source_compiles("${CheckLibAtomicCompiles_CODE}" + HAVE_LIBATOMIC_COMPILES_STATIC) + else() + set(HAVE_LIBATOMIC_COMPILES_STATIC + 0 + CACHE INTERNAL "" FORCE) + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckGCCAtomicIntrinsics_CODE + " + #include + #include + int main(void) + { + ptrdiff_t x = 0; + intmax_t y = 0; + __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); + return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) + + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); + } + ") + + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 + check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS) + + if(NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + if(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + set(HAVE_GCCATOMIC_INTRINSICS + 1 + CACHE INTERNAL "" FORCE) + endif() + endif() endfunction(CheckGCCAtomicIntrinsics) From 167b8e5031ee4e0aed9a49365a7f458e53c1f68b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 29 Sep 2021 19:06:33 +0200 Subject: [PATCH 273/790] [core] Fixed group drift synchronization (#2139) --- srtcore/buffer.cpp | 13 +++++------ srtcore/buffer.h | 8 ++----- srtcore/core.cpp | 7 ++---- srtcore/group.cpp | 49 ++++++++++++------------------------------ srtcore/group.h | 5 ++++- srtcore/tsbpd_time.cpp | 8 +------ srtcore/tsbpd_time.h | 7 +----- 7 files changed, 29 insertions(+), 68 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index bbe41b7f6..c035ad936 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1549,8 +1549,8 @@ CPacket* CRcvBuffer::getRcvReadyPacket(int32_t seqdistance) HLOGC(brlog.Debug, log << "getRcvReadyPacket: Sequence offset=" << seqdistance << " IS NOT RECEIVED."); return 0; } - IF_HEAVY_LOGGING(int nskipped = 0); + IF_HEAVY_LOGGING(int nskipped = 0); for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = shiftFwd(i)) { /* @@ -1559,8 +1559,8 @@ CPacket* CRcvBuffer::getRcvReadyPacket(int32_t seqdistance) if (m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD) { HLOGC(brlog.Debug, - log << "getRcvReadyPacket: Found next packet seq=%" << m_pUnit[i]->m_Packet.getSeqNo() << " (" - << nskipped << " empty cells skipped)"); + log << "getRcvReadyPacket: Found next packet seq=%" << m_pUnit[i]->m_Packet.getSeqNo() << " (" + << nskipped << " empty cells skipped)"); return &m_pUnit[i]->m_Packet; } IF_HEAVY_LOGGING(++nskipped); @@ -1881,12 +1881,9 @@ void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const m_tsbpd.setTsbPdMode(timebase, no_wrap_check, delay); } -bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, - int rtt, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase) +bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, int rtt) { - return m_tsbpd.addDriftSample(timestamp_us, rtt, w_udrift, w_newtimebase); + return m_tsbpd.addDriftSample(timestamp_us, rtt); } int CRcvBuffer::readMsg(char* data, int len) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9bf02f216..2dc63d972 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -411,12 +411,8 @@ class CRcvBuffer /// Add packet timestamp for drift caclculation and compensation /// @param [in] timestamp packet time stamp - /// @param [out] w_udrift current drift value - /// @param [out] w_newtimebase current TSBPD base time - bool addRcvTsbPdDriftSample(uint32_t timestamp, - int rtt, - duration& w_udrift, - time_point& w_newtimebase); + /// @param [in] rtt RTT sample + bool addRcvTsbPdDriftSample(uint32_t timestamp, int rtt); #ifdef SRT_DEBUG_TSBPD_DRIFT void printDriftHistogram(int64_t iDrift); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a5fe649d8..ab1e7da9d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8324,17 +8324,14 @@ 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) { - steady_clock::duration udrift(0); - steady_clock::time_point newtimebase; - const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), - rtt, (udrift), (newtimebase)); + const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), rtt); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { ScopedLock glock(s_UDTUnited.m_GlobControlLock); if (m_parent->m_GroupOf) { - m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); + m_parent->m_GroupOf->synchronizeDrift(this); } } #endif diff --git a/srtcore/group.cpp b/srtcore/group.cpp index e835453ea..bee5c2beb 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2725,48 +2725,24 @@ const char* CUDTGroup::StateStr(CUDTGroup::GroupState st) return unknown; } -void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady_clock::time_point newtimebase) +void CUDTGroup::synchronizeDrift(const srt::CUDT* srcMember) { + SRT_ASSERT(srcMember != NULL); ScopedLock glock(m_GroupLock); - - bool wrap_period = false; - - bool anycheck = false; - - for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) - { - // Skip non-connected; these will be synchronized when ready - if (gi->laststatus != SRTS_CONNECTED) - continue; - - // Skip the entity that has reported this - if (cu == &gi->ps->core()) - continue; - - steady_clock::time_point this_timebase; - steady_clock::duration this_udrift(0); - bool wrp = false; - gi->ps->core().m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); - - udrift = std::min(udrift, this_udrift); - steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); - if (new_newtimebase != newtimebase) - { - wrap_period = wrp; - } - newtimebase = new_newtimebase; - anycheck = true; - } - - if (!anycheck) + if (m_Group.size() <= 1) { HLOGC(grlog.Debug, log << "GROUP: synch uDRIFT NOT DONE, no other links"); return; } + steady_clock::time_point timebase; + steady_clock::duration udrift(0); + bool wrap_period = false; + srcMember->m_pRcvBuffer->getInternalTimeBase((timebase), (wrap_period), (udrift)); + HLOGC(grlog.Debug, - log << "GROUP: synch uDRIFT=" << FormatDuration(udrift) << " TB=" << FormatTime(newtimebase) << "(" - << (wrap_period ? "" : "NO ") << "wrap period)"); + log << "GROUP: synch uDRIFT=" << FormatDuration(udrift) << " TB=" << FormatTime(timebase) << "(" + << (wrap_period ? "" : "NO ") << "wrap period)"); // Now that we have the minimum timebase and drift calculated, apply this to every link, // INCLUDING THE REPORTER. @@ -2776,8 +2752,11 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady // Skip non-connected; these will be synchronized when ready if (gi->laststatus != SRTS_CONNECTED) continue; + CUDT& member = gi->ps->core(); + if (srcMember == &member) + continue; - gi->ps->core().m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); + member.m_pRcvBuffer->applyGroupDrift(timebase, wrap_period, udrift); } } diff --git a/srtcore/group.h b/srtcore/group.h index e4b2fb6ed..04c8e7e01 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -798,7 +798,10 @@ class CUDTGroup // Live state synchronization bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn); - void synchronizeDrift(srt::CUDT* cu, duration udrift, time_point newtimebase); + + /// @brief Synchronize TSBPD base time and clock drift among members using the @a srcMember as a reference. + /// @param srcMember a reference for synchronization. + void synchronizeDrift(const srt::CUDT* srcMember); void updateLatestRcv(srt::CUDTSocket*); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 54a8c5df5..9b75d595e 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -103,10 +103,7 @@ drift_logger g_drift_logger; #endif // SRT_DEBUG_TRACE_DRIFT -bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, - int usRTTSample, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase) +bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, int usRTTSample) { if (!m_bTsbPdMode) return false; @@ -149,9 +146,6 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); } - w_udrift = tdDrift; - w_newtimebase = m_tsTsbPdTimeBase; - #if SRT_DEBUG_TRACE_DRIFT g_drift_logger.trace(usPktTimestamp, usRTTSample, diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h index b6cb770f5..dcaf05718 100644 --- a/srtcore/tsbpd_time.h +++ b/srtcore/tsbpd_time.h @@ -67,14 +67,9 @@ class CTsbpdTime /// /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. /// @param [in] usRTTSample RTT sample from an ACK-ACKACK pair. - /// @param [out] w_udrift Current clock drift value. - /// @param [out] w_newtimebase Current TSBPD base time. /// /// @return true if TSBPD base time has changed, false otherwise. - bool addDriftSample(uint32_t pktTimestamp, - int usRTTSample, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase); + bool addDriftSample(uint32_t pktTimestamp, int usRTTSample); /// @brief Handle timestamp of data packet when 32-bit integer carryover is about to happen. /// When packet timestamp approaches CPacket::MAX_TIMESTAMP, the TSBPD base time should be From 8b32f3734ff6af7cc7b0fef272591cb80a2d1aae Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 1 Oct 2021 14:25:02 +0200 Subject: [PATCH 274/790] [core] Fixed seqno validity check by group receiver (#2142) --- srtcore/common.h | 14 +++++++++----- srtcore/group.cpp | 39 ++++++++++++++++++++++++++++++++++++++- test/test_seqno.cpp | 9 +++------ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/srtcore/common.h b/srtcore/common.h index f6fdba44a..530759dde 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -634,12 +634,16 @@ class CSeqNo /// WITH A PRECONDITION that certainly @a seq1 is earlier than @a seq2. /// This can also include an enormously large distance between them, /// that is, exceeding the m_iSeqNoTH value (can be also used to test - /// if this distance is larger). Prior to calling this function the - /// caller must be certain that @a seq2 is a sequence coming from a - /// later time than @a seq1, and still, of course, this distance didn't - /// exceed m_iMaxSeqNo. + /// if this distance is larger). + /// Prior to calling this function the caller must be certain that + /// @a seq2 is a sequence coming from a later time than @a seq1, + /// and that the distance does not exceed m_iMaxSeqNo. inline static int seqlen(int32_t seq1, int32_t seq2) - {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + { + SRT_ASSERT(seq1 >= 0 && seq1 <= m_iMaxSeqNo); + SRT_ASSERT(seq2 >= 0 && seq2 <= m_iMaxSeqNo); + return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2); + } /// This behaves like seq2 - seq1, with the precondition that the true /// distance between two sequence numbers never exceeds m_iSeqNoTH. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index bee5c2beb..f05f49369 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2148,6 +2148,43 @@ void CUDTGroup::updateWriteState() m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); } +/// Validate iPktSeqno is in range +/// (iBaseSeqno - m_iSeqNoTH/2; iBaseSeqno + m_iSeqNoTH). +/// +/// EXPECT_EQ(isValidSeqno(125, 124), true); // behind +/// EXPECT_EQ(isValidSeqno(125, 125), true); // behind +/// EXPECT_EQ(isValidSeqno(125, 126), true); // the next in order +/// +/// EXPECT_EQ(isValidSeqno(0, 0x3FFFFFFF - 2), true); // ahead, but ok. +/// EXPECT_EQ(isValidSeqno(0, 0x3FFFFFFF - 1), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF + 2, 0x7FFFFFFF), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF + 3, 0x7FFFFFFF), true); // ahead, but ok. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF, 0x1FFFFFFF + 2), false); // too far (behind) +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF, 0x1FFFFFFF + 3), true); // behind, but ok +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x0FFFFFFF), true); // ahead, but ok +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x30000000 - 2), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x30000000 - 3), true); // ahead, but ok +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0), true); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x7FFFFFFF), true); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000000), false); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000001), false); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000002), true); // behind by 536870910 +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000003), true); +/// +/// @return false if @a iPktSeqno is not inside the valid range; otherwise true. +static bool isValidSeqno(int32_t iBaseSeqno, int32_t iPktSeqno) +{ + const int32_t iLenAhead = CSeqNo::seqlen(iBaseSeqno, iPktSeqno); + if (iLenAhead >= 0 && iLenAhead < CSeqNo::m_iSeqNoTH) + return true; + + const int32_t iLenBehind = CSeqNo::seqlen(iPktSeqno, iBaseSeqno); + if (iLenBehind >= 0 && iLenBehind < CSeqNo::m_iSeqNoTH / 2) + return true; + + return false; +} + // The "app reader" version of the reading function. // This reads the packets from every socket treating them as independent // and prepared to work with the application. Then packets are sorted out @@ -2407,7 +2444,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // embrace everything below. // We need to first qualify the sequence, just for a case - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && abs(m_RcvBaseSeqNo - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, mctrl.pktseq)) { // This error should be returned if the link turns out // to be the only one, or set to the group data. diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index f3f50de62..f2e374f76 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -16,7 +16,6 @@ TEST(CSeqNo, constants) EXPECT_EQ(CSeqNo::m_iSeqNoTH, 0x3FFFFFFF); } - TEST(CSeqNo, seqcmp) { // Compare two seq#, considering the wraping. @@ -31,7 +30,6 @@ TEST(CSeqNo, seqcmp) EXPECT_EQ(CSeqNo::seqcmp(1, 0x7FFFFFFF), 0x7FFFFFFE); // 2147483646 } - TEST(CSeqNo, seqoff) { // seqoff: offset from the 2nd to the 1st seq# @@ -48,6 +46,9 @@ TEST(CSeqNo, seqlen) { EXPECT_EQ(CSeqNo::seqlen(125, 125), 1); EXPECT_EQ(CSeqNo::seqlen(125, 126), 2); + + EXPECT_EQ(CSeqNo::seqlen(2147483647, 0), 2); + EXPECT_EQ(CSeqNo::seqlen(0, 2147483647), -2147483648); } TEST(CUDT, getFlightSpan) @@ -74,7 +75,6 @@ TEST(CSeqNo, incseq) EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF), 0x40000000); } - TEST(CSeqNo, decseq) { // decseq: decrease the seq# by 1 @@ -84,7 +84,6 @@ TEST(CSeqNo, decseq) EXPECT_EQ(CSeqNo::decseq(0x40000000), 0x3FFFFFFF); } - TEST(CSeqNo, incseqint) { // incseq: increase the seq# by 1 @@ -98,7 +97,6 @@ TEST(CSeqNo, incseqint) EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 0x40000001), 0x00000000); } - TEST(CSeqNo, decseqint) { // decseq: decrease the seq# by 1 @@ -107,4 +105,3 @@ TEST(CSeqNo, decseqint) EXPECT_EQ(CSeqNo::decseq(0, 1), 0x7FFFFFFF); EXPECT_EQ(CSeqNo::decseq(0x40000000, 1), 0x3FFFFFFF); } - From 1a2aa05c159e72bf2530a7f25cb7a7d6891272df Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 21 Sep 2021 16:09:45 +0200 Subject: [PATCH 275/790] [apps] Print SRT version and clock type --- apps/apputil.cpp | 33 +++++++++++++++++++++++++++++++++ apps/apputil.hpp | 3 +++ apps/srt-file-transmit.cpp | 10 ++-------- apps/srt-live-transmit.cpp | 10 ++-------- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 611b99976..1389b748e 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -684,3 +684,36 @@ SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) return SRTSTATS_PROFMAT_INVALID; } + +const char* SRTClockTypeStr() +{ + const int clock_type = srt_clock_type(); + + switch (clock_type) + { + case SRT_SYNC_CLOCK_STDCXX_STEADY: + return "CXX11_STEADY"; + case SRT_SYNC_CLOCK_GETTIME_MONOTONIC: + return "GETTIME_MONOTONIC"; + case SRT_SYNC_CLOCK_WINQPC: + return "WIN_QPC"; + case SRT_SYNC_CLOCK_MACH_ABSTIME: + return "MACH_ABSTIME"; + case SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY: + return "POSIX_GETTIMEOFDAY"; + default: + break; + } + + return "UNKNOWN VALUE"; +} + +void PrintLibVersion() +{ + cerr << "Built with SRT Library version: " << SRT_VERSION << endl; + const uint32_t srtver = srt_getversion(); + const int major = srtver / 0x10000; + const int minor = (srtver / 0x100) % 0x100; + const int patch = srtver % 0x100; + cerr << "SRT Library version: " << major << "." << minor << "." << patch << ", clock type: " << SRTClockTypeStr() << endl; +} diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 5bac461e0..782412815 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -419,5 +419,8 @@ extern std::vector> g_SrtStatsTable; std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); +const char* SRTClockTypeStr(); +void PrintLibVersion(); + #endif // INC_SRT_APPCOMMON_H diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index d3ea92092..327ad6809 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -52,7 +52,6 @@ void OnINT_ForceExit(int) interrupt = true; } - struct FileTransmitConfig { unsigned long chunk_size; @@ -144,12 +143,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) if (print_help) { cout << "SRT sample application to transmit files.\n"; - cerr << "Built with SRT Library version: " << SRT_VERSION << endl; - const uint32_t srtver = srt_getversion(); - const int major = srtver / 0x10000; - const int minor = (srtver / 0x100) % 0x100; - const int patch = srtver % 0x100; - cerr << "SRT Library version: " << major << "." << minor << "." << patch << endl; + PrintLibVersion(); cerr << "Usage: srt-file-transmit [options] \n"; cerr << "\n"; @@ -182,7 +176,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) if (Option(params, false, o_version)) { - cerr << "SRT Library version: " << SRT_VERSION << endl; + PrintLibVersion(); return 2; } diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 9aad3209c..6d5ee18de 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -164,7 +164,6 @@ void PrintOptionHelp(const OptionName& opt_names, const string &value, const str cerr << "\t- " << desc << "\n"; } - int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) { const OptionName @@ -269,12 +268,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) } cout << "SRT sample application to transmit live streaming.\n"; - cerr << "Built with SRT Library version: " << SRT_VERSION << endl; - const uint32_t srtver = srt_getversion(); - const int major = srtver / 0x10000; - const int minor = (srtver / 0x100) % 0x100; - const int patch = srtver % 0x100; - cerr << "SRT Library version: " << major << "." << minor << "." << patch << endl; + PrintLibVersion(); cerr << "Usage: srt-live-transmit [options] \n"; cerr << "\n"; #ifndef _WIN32 @@ -313,7 +307,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) if (print_version) { - cerr << "SRT Library version: " << SRT_VERSION << endl; + PrintLibVersion(); return 2; } From 0c604c1ec51f8c4ed7918d664abf5dad7a0126bf Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 10 Sep 2021 15:56:16 +0200 Subject: [PATCH 276/790] [core] Moved SND and RCV buffers into the 'srt' namespace --- srtcore/buffer.cpp | 23 ++++++++++-------- srtcore/buffer.h | 60 ++++++++++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index c035ad936..329b054a0 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -59,10 +59,11 @@ modified by #include "core.h" // provides some constants #include "logging.h" +namespace srt { + using namespace std; using namespace srt_logging; -using namespace srt; -using namespace srt::sync; +using namespace sync; // You can change this value at build config by using "ENFORCE" options. #if !defined(SRT_MAVG_SAMPLING_RATE) @@ -318,7 +319,7 @@ void CSndBuffer::updateInputRate(const steady_clock::time_point& time, int pkts, if (early_update || period_us > m_InRatePeriod) { // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * srt::CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount @@ -411,7 +412,7 @@ steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& bloc return block.m_tsOriginTime; } -int CSndBuffer::readData(srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read if (m_pCurrBlock == m_pLastBlock) @@ -512,7 +513,7 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, srt::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, int& w_msglen) { int32_t& msgno_bitset = w_packet.m_iMsgNo; @@ -605,7 +606,7 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: return readlen; } -srt::sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) +sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) { ScopedLock bufferguard(m_BufLock); const Block* p = m_pFirstBlock; @@ -935,7 +936,7 @@ int CRcvBuffer::readBuffer(char* data, int len) return -1; } - const srt::CPacket& pkt = m_pUnit[p]->m_Packet; + const CPacket& pkt = m_pUnit[p]->m_Packet; if (bTsbPdEnabled) { @@ -1004,7 +1005,7 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) continue; } - const srt::CPacket& pkt = m_pUnit[p]->m_Packet; + const CPacket& pkt = m_pUnit[p]->m_Packet; #if ENABLE_LOGGING trace_seq = pkt.getSeqNo(); @@ -1476,7 +1477,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& if (m_tsbpd.isEnabled()) { - const srt::CPacket* pkt = getRcvReadyPacket(seqdistance); + const CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) { HLOGC(brlog.Debug, log << "isRcvDataReady: packet NOT extracted."); @@ -1613,7 +1614,7 @@ void CRcvBuffer::reportBufferStats() const uint64_t lower_time = low_ts; if (lower_time > upper_time) - upper_time += uint64_t(srt::CPacket::MAX_TIMESTAMP) + 1; + upper_time += uint64_t(CPacket::MAX_TIMESTAMP) + 1; int32_t timespan = upper_time - lower_time; int seqspan = 0; @@ -2287,3 +2288,5 @@ bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) return found; } + +} // namespace srt diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 2dc63d972..8756a49d1 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -71,10 +71,12 @@ modified by // a +% b : shift a by b // a == b : equality is same as for just numbers +namespace srt { + /// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND) class AvgBufSize { - typedef srt::sync::steady_clock::time_point time_point; + typedef sync::steady_clock::time_point time_point; public: AvgBufSize() @@ -102,8 +104,8 @@ class AvgBufSize class CSndBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; public: // XXX There's currently no way to access the socket ID set for @@ -140,21 +142,19 @@ class CSndBuffer int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] data the pointer to the data position. - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [in] kflags Odd|Even crypto key flag + /// @param [out] w_packet data packet buffer to fill. + /// @param [out] w_origintime origin time stamp of the message. + /// @param [in] kflags Odd|Even crypto key flag. /// @return Actual length of data read. - int readData(srt::CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); /// Find data position to pack a DATA packet for a retransmission. - /// @param [out] data the pointer to the data position. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message + /// @param [out] w_packet data packet buffer to fill. + /// @param [out] w_origintime origin time stamp of the message + /// @param [out] w_msglen length of the message /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). - int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// 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) @@ -207,7 +207,7 @@ class CSndBuffer static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - srt::sync::Mutex m_BufLock; // used to synchronize buffer operation + sync::Mutex m_BufLock; // used to synchronize buffer operation struct Block { @@ -274,8 +274,8 @@ class CSndBuffer class CRcvBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; public: // XXX There's currently no way to access the socket ID set for @@ -288,7 +288,7 @@ class CRcvBuffer /// Construct the buffer. /// @param [in] queue CUnitQueue that actually holds the units (packets) /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(srt::CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); ~CRcvBuffer(); public: @@ -296,7 +296,7 @@ class CRcvBuffer /// @param [in] unit pointer to a data unit containing new packet /// @param [in] offset offset from last ACK point. /// @return 0 is success, -1 if data is repeated. - int addData(srt::CUnit* unit, int offset); + int addData(CUnit* unit, int offset); /// Read data into a user buffer. /// @param [in] data pointer to user buffer. @@ -402,7 +402,7 @@ class CRcvBuffer bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } - srt::CPacket* getRcvReadyPacket(int32_t seqdistance); + CPacket* getRcvReadyPacket(int32_t seqdistance); /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay @@ -464,7 +464,7 @@ class CRcvBuffer /// data. size_t freeUnitAt(size_t p) { - srt::CUnit* u = m_pUnit[p]; + CUnit* u = m_pUnit[p]; m_pUnit[p] = NULL; size_t rmbytes = u->m_Packet.getLength(); m_pUnitQueue->makeUnitFree(u); @@ -531,9 +531,9 @@ class CRcvBuffer } private: - srt::CUnit** m_pUnit; // Array of pointed units collected in the buffer + CUnit** m_pUnit; // Array of pointed units collected in the buffer const int m_iSize; // Size of the internal array of CUnit* items - srt::CUnitQueue* m_pUnitQueue; // the shared unit queue + CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartPos; // HEAD: first packet available for reading int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable @@ -546,15 +546,15 @@ class CRcvBuffer // up to which data are already retrieved; // in message reading mode it's unused and always 0) - srt::sync::Mutex m_BytesCountLock; // used to protect counters operations - int m_iBytesCount; // Number of payload bytes in the buffer - int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer - int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation + sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer + int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - srt::CTsbpdTime m_tsbpd; + CTsbpdTime m_tsbpd; - AvgBufSize m_mavg; + AvgBufSize m_mavg; private: CRcvBuffer(); @@ -562,4 +562,6 @@ class CRcvBuffer CRcvBuffer& operator=(const CRcvBuffer&); }; +} // namespace srt + #endif From 5b0811ca33cecb9cc2d1088ca6158277c599b317 Mon Sep 17 00:00:00 2001 From: quink-black Date: Mon, 4 Oct 2021 21:01:55 +0800 Subject: [PATCH 277/790] [core] Replaced global CUDTUnited with static local variable (#1913) A static local variable has the benefits of lazy initialization and avoids the initialization order problem of global variables. --- srtcore/api.cpp | 132 ++++++++++++++-------------- srtcore/core.cpp | 147 ++++++++++++++++++------------- srtcore/core.h | 5 +- srtcore/group.cpp | 162 +++++++++++++++++------------------ srtcore/group.h | 4 +- srtcore/queue.cpp | 2 +- test/test_fec_rebuilding.cpp | 4 +- 7 files changed, 241 insertions(+), 215 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 71386a566..38b86cf8c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -846,7 +846,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // static forwarder int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { - return s_UDTUnited.installAcceptHook(lsn, hook, opaq); + return uglobal().installAcceptHook(lsn, hook, opaq); } int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) @@ -867,7 +867,7 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { - return s_UDTUnited.installConnectHook(lsn, hook, opaq); + return uglobal().installConnectHook(lsn, hook, opaq); } int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) @@ -3210,22 +3210,22 @@ void* srt::CUDTUnited::garbageCollect(void* p) int srt::CUDT::startup() { - return s_UDTUnited.startup(); + return uglobal().startup(); } int srt::CUDT::cleanup() { - return s_UDTUnited.cleanup(); + return uglobal().cleanup(); } SRTSOCKET srt::CUDT::socket() { - if (!s_UDTUnited.m_bGCStatus) - s_UDTUnited.startup(); + if (!uglobal().m_bGCStatus) + uglobal().startup(); try { - return s_UDTUnited.newSocket(); + return uglobal().newSocket(); } catch (const CUDTException& e) { @@ -3264,21 +3264,21 @@ srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) // [[using locked(s_UDTUnited.m_GlobControlLock)]] srt::CUDTGroup& srt::CUDT::newGroup(const int type) { - const SRTSOCKET id = s_UDTUnited.generateSocketID(true); + const SRTSOCKET id = uglobal().generateSocketID(true); // Now map the group - return s_UDTUnited.addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); + return uglobal().addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); } SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() - if (!s_UDTUnited.m_bGCStatus) - s_UDTUnited.startup(); + if (!uglobal().m_bGCStatus) + uglobal().startup(); try { - srt::sync::ScopedLock globlock (s_UDTUnited.m_GlobControlLock); + srt::sync::ScopedLock globlock (uglobal().m_GlobControlLock); return newGroup(gt).id(); // Note: potentially, after this function exits, the group // could be deleted, immediately, from a separate thread (tho @@ -3309,8 +3309,8 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Find the socket and the group - CUDTSocket* s = s_UDTUnited.locateSocket(socket); - CUDTUnited::GroupKeeper k (s_UDTUnited, group, s_UDTUnited.ERH_RETURN); + CUDTSocket* s = uglobal().locateSocket(socket); + CUDTUnited::GroupKeeper k (uglobal(), group, CUDTUnited::ERH_RETURN); if (!s || !k.group) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3331,7 +3331,7 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) } ScopedLock cg (s->m_ControlLock); - ScopedLock cglob (s_UDTUnited.m_GlobControlLock); + ScopedLock cglob (uglobal().m_GlobControlLock); if (g->closing()) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3355,7 +3355,7 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) // groups. int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) { - CUDTSocket* s = s_UDTUnited.locateSocket(socket); + CUDTSocket* s = uglobal().locateSocket(socket); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3363,7 +3363,7 @@ int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) return APIError(MJ_NOTSUP, MN_INVAL, 0); ScopedLock cg (s->m_ControlLock); - ScopedLock glob_grd (s_UDTUnited.m_GlobControlLock); + ScopedLock glob_grd (uglobal().m_GlobControlLock); s->removeFromGroup(false); return 0; } @@ -3403,8 +3403,8 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) { // Lock this for the whole function as we need the group // to persist the call. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); - CUDTSocket* s = s_UDTUnited.locateSocket_LOCKED(socket); + ScopedLock glock (uglobal().m_GlobControlLock); + CUDTSocket* s = uglobal().locateSocket_LOCKED(socket); if (!s || !s->m_GroupOf) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3418,7 +3418,7 @@ int srt::CUDT::configureGroup(SRTSOCKET groupid, const char* str) return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTUnited::GroupKeeper k (s_UDTUnited, groupid, s_UDTUnited.ERH_RETURN); + CUDTUnited::GroupKeeper k (uglobal(), groupid, CUDTUnited::ERH_RETURN); if (!k.group) { return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3434,7 +3434,7 @@ int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTUnited::GroupKeeper k (s_UDTUnited, groupid, s_UDTUnited.ERH_RETURN); + CUDTUnited::GroupKeeper k (uglobal(), groupid, CUDTUnited::ERH_RETURN); if (!k.group) { return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3458,11 +3458,11 @@ int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) // This is a user error. return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); - return s_UDTUnited.bind(s, sa); + return uglobal().bind(s, sa); } catch (const CUDTException& e) { @@ -3485,11 +3485,11 @@ int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); - return s_UDTUnited.bind(s, udpsock); + return uglobal().bind(s, udpsock); } catch (const CUDTException& e) { @@ -3511,7 +3511,7 @@ int srt::CUDT::listen(SRTSOCKET u, int backlog) { try { - return s_UDTUnited.listen(u, backlog); + return uglobal().listen(u, backlog); } catch (const CUDTException& e) { @@ -3533,7 +3533,7 @@ SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_ { try { - return s_UDTUnited.accept_bond(listeners, lsize, msTimeOut); + return uglobal().accept_bond(listeners, lsize, msTimeOut); } catch (const CUDTException& e) { @@ -3558,7 +3558,7 @@ SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) { try { - return s_UDTUnited.accept(u, addr, addrlen); + return uglobal().accept(u, addr, addrlen); } catch (const CUDTException& e) { @@ -3584,7 +3584,7 @@ int srt::CUDT::connect( { try { - return s_UDTUnited.connect(u, name, tname, namelen); + return uglobal().connect(u, name, tname, namelen); } catch (const CUDTException& e) { @@ -3617,8 +3617,8 @@ int srt::CUDT::connectLinks(SRTSOCKET grp, try { - CUDTUnited::GroupKeeper k(s_UDTUnited, grp, s_UDTUnited.ERH_THROW); - return s_UDTUnited.groupConnect(k.group, targets, arraysize); + CUDTUnited::GroupKeeper k(uglobal(), grp, CUDTUnited::ERH_THROW); + return uglobal().groupConnect(k.group, targets, arraysize); } catch (CUDTException& e) { @@ -3642,7 +3642,7 @@ int srt::CUDT::connect( { try { - return s_UDTUnited.connect(u, name, namelen, forced_isn); + return uglobal().connect(u, name, namelen, forced_isn); } catch (const CUDTException &e) { @@ -3664,7 +3664,7 @@ int srt::CUDT::close(SRTSOCKET u) { try { - return s_UDTUnited.close(u); + return uglobal().close(u); } catch (const CUDTException& e) { @@ -3682,7 +3682,7 @@ int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { - s_UDTUnited.getpeername(u, name, namelen); + uglobal().getpeername(u, name, namelen); return 0; } catch (const CUDTException& e) @@ -3701,7 +3701,7 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { - s_UDTUnited.getsockname(u, name, namelen); + uglobal().getsockname(u, name, namelen); return 0; } catch (const CUDTException& e) @@ -3729,13 +3729,13 @@ int srt::CUDT::getsockopt( #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } #endif - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } @@ -3761,13 +3761,13 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); return 0; } #endif - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.setOpt(optname, optval, optlen); return 0; } @@ -3810,12 +3810,12 @@ int srt::CUDT::sendmsg2( #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k (s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k (uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); } #endif - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().sendmsg2(buf, len, (w_m)); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().sendmsg2(buf, len, (w_m)); } catch (const CUDTException& e) { @@ -3855,12 +3855,12 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); } #endif - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().recvmsg2(buf, len, (w_m)); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().recvmsg2(buf, len, (w_m)); } catch (const CUDTException& e) { @@ -3879,7 +3879,7 @@ int64_t srt::CUDT::sendfile( { try { - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); return udt.sendfile(ifs, offset, size, block); } catch (const CUDTException& e) @@ -3903,7 +3903,7 @@ int64_t srt::CUDT::recvfile( { try { - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().recvfile(ofs, offset, size, block); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().recvfile(ofs, offset, size, block); } catch (const CUDTException& e) { @@ -3931,7 +3931,7 @@ int srt::CUDT::select( try { - return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); + return uglobal().select(readfds, writefds, exceptfds, timeout); } catch (const CUDTException& e) { @@ -3963,7 +3963,7 @@ int srt::CUDT::selectEx( try { - return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + return uglobal().selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } catch (const CUDTException& e) { @@ -3985,7 +3985,7 @@ int srt::CUDT::epoll_create() { try { - return s_UDTUnited.epoll_create(); + return uglobal().epoll_create(); } catch (const CUDTException& e) { @@ -4003,7 +4003,7 @@ int srt::CUDT::epoll_clear_usocks(int eid) { try { - return s_UDTUnited.epoll_clear_usocks(eid); + return uglobal().epoll_clear_usocks(eid); } catch (const CUDTException& e) { @@ -4021,7 +4021,7 @@ int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* even { try { - return s_UDTUnited.epoll_add_usock(eid, u, events); + return uglobal().epoll_add_usock(eid, u, events); } catch (const CUDTException& e) { @@ -4039,7 +4039,7 @@ int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* even { try { - return s_UDTUnited.epoll_add_ssock(eid, s, events); + return uglobal().epoll_add_ssock(eid, s, events); } catch (const CUDTException& e) { @@ -4058,7 +4058,7 @@ int srt::CUDT::epoll_update_usock( { try { - return s_UDTUnited.epoll_add_usock(eid, u, events); + return uglobal().epoll_add_usock(eid, u, events); } catch (const CUDTException& e) { @@ -4077,7 +4077,7 @@ int srt::CUDT::epoll_update_ssock( { try { - return s_UDTUnited.epoll_update_ssock(eid, s, events); + return uglobal().epoll_update_ssock(eid, s, events); } catch (const CUDTException& e) { @@ -4096,7 +4096,7 @@ int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { - return s_UDTUnited.epoll_remove_usock(eid, u); + return uglobal().epoll_remove_usock(eid, u); } catch (const CUDTException& e) { @@ -4114,7 +4114,7 @@ int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { - return s_UDTUnited.epoll_remove_ssock(eid, s); + return uglobal().epoll_remove_ssock(eid, s); } catch (const CUDTException& e) { @@ -4138,7 +4138,7 @@ int srt::CUDT::epoll_wait( { try { - return s_UDTUnited.epoll_ref().wait( + return uglobal().epoll_ref().wait( eid, readfds, writefds, msTimeOut, lrfds, lwfds); } catch (const CUDTException& e) @@ -4161,7 +4161,7 @@ int srt::CUDT::epoll_uwait( { try { - return s_UDTUnited.epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); + return uglobal().epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); } catch (const CUDTException& e) { @@ -4181,7 +4181,7 @@ int32_t srt::CUDT::epoll_set( { try { - return s_UDTUnited.epoll_set(eid, flags); + return uglobal().epoll_set(eid, flags); } catch (const CUDTException& e) { @@ -4199,7 +4199,7 @@ int srt::CUDT::epoll_release(const int eid) { try { - return s_UDTUnited.epoll_release(eid); + return uglobal().epoll_release(eid); } catch (const CUDTException& e) { @@ -4227,7 +4227,7 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta try { - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.bstats(perf, clear, instantaneous); return 0; } @@ -4248,7 +4248,7 @@ int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->bstatsSocket(perf, clear); return 0; } @@ -4271,7 +4271,7 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { - return &s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + return &uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); } catch (const CUDTException& e) { @@ -4290,8 +4290,8 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) vector srt::CUDT::existingSockets() { vector out; - for (CUDTUnited::sockets_t::iterator i = s_UDTUnited.m_Sockets.begin(); - i != s_UDTUnited.m_Sockets.end(); ++i) + for (CUDTUnited::sockets_t::iterator i = uglobal().m_Sockets.begin(); + i != uglobal().m_Sockets.end(); ++i) { out.push_back(i->first); } @@ -4305,11 +4305,11 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) #if ENABLE_EXPERIMENTAL_BONDING if (isgroup(u)) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->getStatus(); } #endif - return s_UDTUnited.getStatus(u); + return uglobal().getStatus(u); } catch (const CUDTException& e) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ab1e7da9d..53e4a28d5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -70,6 +70,11 @@ modified by #include "logging_api.h" // Required due to containing extern srt_logger_config #include "logger_defs.h" +#if !HAVE_CXX11 +// for pthread_once +#include +#endif + // Again, just in case when some "smart guy" provided such a global macro #ifdef min #undef min @@ -83,10 +88,6 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -namespace srt { - CUDTUnited CUDT::s_UDTUnited; -} - const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; @@ -222,6 +223,32 @@ const SrtOptionAction s_sockopt_action; } // namespace srt +#if HAVE_CXX11 + +CUDTUnited& srt::CUDT::uglobal() +{ + static CUDTUnited instance; + return instance; +} + +#else // !HAVE_CXX11 + +static pthread_once_t s_UDTUnitedOnce = PTHREAD_ONCE_INIT; + +static CUDTUnited *getInstance() +{ + static CUDTUnited instance; + return &instance; +} + +CUDTUnited& srt::CUDT::uglobal() +{ + // We don't want lock each time, pthread_once can be faster than mutex. + pthread_once(&s_UDTUnitedOnce, reinterpret_cast(getInstance)); + return *getInstance(); +} + +#endif void srt::CUDT::construct() { @@ -512,7 +539,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_STATE: - *(int32_t *)optval = s_UDTUnited.getStatus(m_SocketID); + *(int32_t *)optval = uglobal().getStatus(m_SocketID); optlen = sizeof(int32_t); break; @@ -1692,7 +1719,7 @@ bool srt::CUDT::createSrtHandshake( if (have_group) { // NOTE: See information about mutex ordering in api.h - ScopedLock gdrg (s_UDTUnited.m_GlobControlLock); + ScopedLock gdrg (uglobal().m_GlobControlLock); if (!m_parent->m_GroupOf) { // This may only happen if since last check of m_GroupOf pointer the socket was removed @@ -3039,7 +3066,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A return false; } - ScopedLock guard_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock guard_group_existence (uglobal().m_GlobControlLock); if (m_SrtHsSide == HSD_INITIATOR) { @@ -3161,7 +3188,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // it right now so there's no need to lock s->m_ControlLock. // Check if there exists a group that this one is a peer of. - CUDTGroup* gp = s_UDTUnited.findPeerGroup_LOCKED(peergroup); + CUDTGroup* gp = uglobal().findPeerGroup_LOCKED(peergroup); bool was_empty = true; if (gp) { @@ -3192,7 +3219,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. - s_UDTUnited.deleteGroup_LOCKED(gp); + uglobal().deleteGroup_LOCKED(gp); return -1; } @@ -4569,7 +4596,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, { #if ENABLE_EXPERIMENTAL_BONDING - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -4619,7 +4646,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // the socket could have been started removal before this function // has started. Do a sanity check before you continue with the // connection process. - CUDTSocket* s = s_UDTUnited.locateSocket(m_SocketID); + CUDTSocket* s = uglobal().locateSocket(m_SocketID); if (s) { // The socket could be closed at this very moment. @@ -4676,7 +4703,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, //int token = -1; #if ENABLE_EXPERIMENTAL_BONDING { - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -4705,7 +4732,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, s->m_Status = SRTS_CONNECTED; // acknowledde any waiting epolls to write - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_CONNECT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_CONNECT, true); CGlobEvent::triggerEvent(); @@ -5150,7 +5177,7 @@ void * srt::CUDT::tsbpd(void *param) // which will ensure that the group will not be physically // deleted until this thread exits. // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!! - CUDTUnited::GroupKeeper gkeeper (self->s_UDTUnited, self->m_parent); + CUDTUnited::GroupKeeper gkeeper (self->uglobal(), self->m_parent); #endif UniqueLock recv_lock (self->m_RecvLock); @@ -5268,7 +5295,7 @@ void * srt::CUDT::tsbpd(void *param) /* * Set EPOLL_IN to wakeup any thread waiting on epoll */ - self->s_UDTUnited.m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); + self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); #if ENABLE_EXPERIMENTAL_BONDING // If this is NULL, it means: // - the socket never was a group member @@ -5545,7 +5572,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& { #if ENABLE_EXPERIMENTAL_BONDING - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -5902,13 +5929,13 @@ bool srt::CUDT::closeInternal() // Make a copy under a lock because other thread might access it // at the same time. - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); set epollid = m_sPollID; - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); // trigger any pending IO events. HLOGC(smlog.Debug, log << "close: SETTING ERR readiness on E" << Printable(epollid) << " of @" << m_SocketID); - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); // then remove itself from all epoll monitoring int no_events = 0; for (set::iterator i = epollid.begin(); i != epollid.end(); ++i) @@ -5916,7 +5943,7 @@ bool srt::CUDT::closeInternal() HLOGC(smlog.Debug, log << "close: CLEARING subscription on E" << (*i) << " of @" << m_SocketID); try { - s_UDTUnited.m_EPoll.update_usock(*i, m_SocketID, &no_events); + uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events); } catch (...) { @@ -5933,9 +5960,9 @@ bool srt::CUDT::closeInternal() // IMPORTANT: there's theoretically little time between setting ERR readiness // and unsubscribing, however if there's an application waiting on this event, // it should be informed before this below instruction locks the epoll mutex. - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.clear(); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); // XXX What's this, could any of the above actions make it !m_bOpened? if (!m_bOpened) @@ -6128,7 +6155,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } if ((res <= 0) && (m_config.iRcvTimeOut >= 0)) @@ -6515,7 +6542,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (sndBuffersLeft() < 1) // XXX Not sure if it should test if any space in the buffer, or as requried. { // write is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); } } @@ -6638,7 +6665,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } if (res == 0) @@ -6679,7 +6706,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); // Forced to return 0 instead of throwing exception, in case of AGAIN/READ if (!by_exception) return 0; @@ -6700,7 +6727,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING @@ -6819,7 +6846,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } // Unblock when required @@ -6948,7 +6975,7 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (sndBuffersLeft() <= 0) { // write is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); } } @@ -7074,7 +7101,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } return size - torecv; @@ -7745,7 +7772,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // can be either never set, already reset, or ever set // and possibly dangling. The re-check after lock eliminates // the dangling case. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); // Note that updateLatestRcv will lock m_GroupOf->m_GroupLock, // but this is an intended order. @@ -7797,13 +7824,13 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // (4) receive thread: receive data and set SRT_EPOLL_IN to true // (5) user thread: set SRT_EPOLL_IN to false // 4. so , m_RecvLock must be used here to protect epoll event - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { // See above explanation for double-checking - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { @@ -7953,7 +7980,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) m_pSndBuffer->ackData(offset); // acknowledde any waiting epolls to write - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); CGlobEvent::triggerEvent(); } @@ -7962,7 +7989,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) { // m_RecvAckLock is ordered AFTER m_GlobControlLock, so this can only // be done now that m_RecvAckLock is unlocked. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { HLOGC(inlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); @@ -8111,7 +8138,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { // Will apply m_GroupLock, ordered after m_GlobControlLock. @@ -8328,7 +8355,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { - ScopedLock glock(s_UDTUnited.m_GlobControlLock); + ScopedLock glock(uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { m_parent->m_GroupOf->synchronizeDrift(this); @@ -8827,7 +8854,7 @@ void srt::CUDT::updateAfterSrtHandshake(int hsv) if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); grpspec = m_parent->m_GroupOf ? " group=$" + Sprint(m_parent->m_GroupOf->id()) : string(); @@ -9272,7 +9299,7 @@ void srt::CUDT::processClose() // Signal the sender and recver if they are waiting for data. releaseSynch(); // Unblock any call so they learn the connection_broken error - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); HLOGP(smlog.Debug, "processClose: triggering timer event to spread the bad news"); CGlobEvent::triggerEvent(); @@ -9547,7 +9574,7 @@ int srt::CUDT::processData(CUnit* in_unit) // reception sequence pointer stating that this link is not receiving. if (m_parent->m_GroupOf) { - ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock protect_group_existence (uglobal().m_GlobControlLock); groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket @@ -10563,7 +10590,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { int error = SRT_REJ_UNKNOWN; CUDT* acpu = NULL; - int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); + int result = uglobal().newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); // This is listener - m_RejectReason need not be set // because listener has no functionality of giving the app @@ -10703,7 +10730,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // Note: not using SRT_EPOLL_CONNECT symbol because this is a procedure // executed for the accepted socket. - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } } LOGC(cnlog.Note, log << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); @@ -11003,7 +11030,7 @@ void srt::CUDT::checkTimers() #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { // Pass socket ID because it's about changing group socket data @@ -11023,7 +11050,7 @@ void srt::CUDT::updateBrokenConnection() m_bClosing = true; releaseSynch(); // app can call any UDT API to learn the connection_broken error - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); CGlobEvent::triggerEvent(); } @@ -11034,7 +11061,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) #if ENABLE_EXPERIMENTAL_BONDING bool pending_broken = false; { - ScopedLock guard_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock guard_group_existence (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { token = m_parent->m_GroupMemberData->token; @@ -11066,7 +11093,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // existence of the group will not be changed during // the operation. The attempt of group deletion will // have to wait until this operation completes. - ScopedLock lock(s_UDTUnited.m_GlobControlLock); + ScopedLock lock(uglobal().m_GlobControlLock); CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { @@ -11079,8 +11106,8 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // explicitly, otherwise they will never be deleted. if (pending_broken) { - // XXX This somehow can cause a deadlock, even without GlobControlLock - // s_UDTUnited.close(m_parent); + // XXX This somehow can cause a deadlock + // uglobal()->close(m_parent); m_parent->setBrokenClosed(); } #endif @@ -11088,9 +11115,9 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) void srt::CUDT::addEPoll(const int eid) { - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.insert(eid); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); if (!stillConnected()) return; @@ -11098,13 +11125,13 @@ void srt::CUDT::addEPoll(const int eid) enterCS(m_RecvLock); if (m_pRcvBuffer->isRcvDataReady()) { - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } leaveCS(m_RecvLock); if (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize()) { - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } } @@ -11114,14 +11141,14 @@ void srt::CUDT::removeEPollEvents(const int eid) // since this happens after the epoll ID has been removed, they cannot be set again set remove; remove.insert(eid); - s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } void srt::CUDT::removeEPollID(const int eid) { - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.erase(eid); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); } void srt::CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) @@ -11150,7 +11177,7 @@ void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { - CUDTSocket *s = s_UDTUnited.locateSocket(u); + CUDTSocket *s = uglobal().locateSocket(u); if (!s) return -1; @@ -11173,7 +11200,7 @@ int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) int srt::CUDT::rejectReason(SRTSOCKET u) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return SRT_REJ_UNKNOWN; @@ -11182,7 +11209,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u) int srt::CUDT::rejectReason(SRTSOCKET u, int value) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); @@ -11195,7 +11222,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u, int value) int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); @@ -11316,7 +11343,7 @@ void srt::CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) // existence of the group will not be changed during // the operation. The attempt of group deletion will // have to wait until this operation completes. - ScopedLock lock(s_UDTUnited.m_GlobControlLock); + ScopedLock lock(uglobal().m_GlobControlLock); CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { diff --git a/srtcore/core.h b/srtcore/core.h index a9d2f9dc3..1fd5f6730 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -409,8 +409,8 @@ class CUDT return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } - // For SRT_tsbpdLoop - static CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop + static CUDTUnited& uglobal(); // UDT global management base + std::set& pollset() { return m_sPollID; } CSrtConfig m_config; @@ -697,7 +697,6 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - static CUDTUnited s_UDTUnited; // UDT global management base private: // Identification CUDTSocket* const m_parent; // Temporary, until the CUDTSocket class is merged with CUDT diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f05f49369..c27ae2cbe 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -248,7 +248,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) } CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) - : m_pGlobal(&CUDT::s_UDTUnited) + : m_Global(CUDT::uglobal()) , m_GroupID(-1) , m_PeerGroupID(-1) , m_selfManaged(true) @@ -282,8 +282,8 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) setupMutex(m_GroupLock, "Group"); setupMutex(m_RcvDataLock, "RcvData"); setupCond(m_RcvDataCond, "RcvData"); - m_RcvEID = m_pGlobal->m_EPoll.create(&m_RcvEpolld); - m_SndEID = m_pGlobal->m_EPoll.create(&m_SndEpolld); + m_RcvEID = m_Global.m_EPoll.create(&m_RcvEpolld); + m_SndEID = m_Global.m_EPoll.create(&m_SndEpolld); m_stats.init(); @@ -869,7 +869,7 @@ SRT_SOCKSTATUS CUDTGroup::getStatus() if (i->second == SRTS_NONEXIST) { // Otherwise find at least one socket, which's state isn't broken. - i->second = m_pGlobal->getStatus(i->first); + i->second = m_Global.getStatus(i->first); if (pending_state == SRTS_NONEXIST) pending_state = i->second; } @@ -920,7 +920,7 @@ void CUDTGroup::close() vector ids; { - ScopedLock glob(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock glob(CUDT::uglobal().m_GlobControlLock); ScopedLock g(m_GroupLock); // A non-managed group may only be closed if there are no @@ -941,7 +941,7 @@ void CUDTGroup::close() ids.push_back(ig->id); // Immediately cut ties to this group. // Just for a case, redispatch the socket, to stay safe. - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(ig->id); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(ig->id); if (!s) { HLOGC(smlog.Debug, log << "group/close: IPE(NF): group member @" << ig->id << " already deleted"); @@ -963,7 +963,7 @@ void CUDTGroup::close() { // Global EPOLL lock must be applied to access any socket's epoll set. // This is a set of all epoll ids subscribed to it. - ScopedLock elock (CUDT::s_UDTUnited.m_EPoll.m_EPollLock); + ScopedLock elock (CUDT::uglobal().m_EPoll.m_EPollLock); epollid = m_sPollID; // use move() in C++11 m_sPollID.clear(); } @@ -974,7 +974,7 @@ void CUDTGroup::close() HLOGC(smlog.Debug, log << "close: CLEARING subscription on E" << (*i) << " of $" << id()); try { - CUDT::s_UDTUnited.m_EPoll.update_usock(*i, id(), &no_events); + CUDT::uglobal().m_EPoll.update_usock(*i, id(), &no_events); } catch (...) { @@ -994,7 +994,7 @@ void CUDTGroup::close() { try { - CUDT::s_UDTUnited.close(*i); + CUDT::uglobal().close(*i); } catch (CUDTException&) { @@ -1026,7 +1026,7 @@ void CUDTGroup::close() // CSync::lock_signal(m_RcvDataCond, m_RcvDataLock); } -// [[using locked(m_pGlobal->m_GlobControlLock)]] +// [[using locked(m_Global->m_GlobControlLock)]] // [[using locked(m_GroupLock)]] void CUDTGroup::send_CheckValidSockets() { @@ -1035,7 +1035,7 @@ void CUDTGroup::send_CheckValidSockets() for (gli_t d = m_Group.begin(), d_next = d; d != m_Group.end(); d = d_next) { ++d_next; // it's now safe to erase d - CUDTSocket* revps = m_pGlobal->locateSocket_LOCKED(d->id); + CUDTSocket* revps = m_Global.locateSocket_LOCKED(d->id); if (revps != d->ps) { // Note: the socket might STILL EXIST, just in the trash, so @@ -1104,12 +1104,12 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) vector activeLinks; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) { - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -1117,7 +1117,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // LOCKED: GlobControlLock, GroupLock (RIGHT ORDER!) send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); // LOCKED: GroupLock (only) // Since this moment GlobControlLock may only be locked if GroupLock is unlocked first. @@ -1355,7 +1355,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // at the connecting stage. CEPoll::fmap_t sready; - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, @@ -1368,7 +1368,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) InvertedLock ug(m_GroupLock); THREAD_PAUSED(); - m_pGlobal->m_EPoll.swait( + m_Global.m_EPoll.swait( *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened THREAD_RESUMED(); } @@ -1391,7 +1391,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Failed socket. Move d to wipeme. Remove from eid. wipeme.push_back(*i); int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); + m_Global.m_EPoll.update_usock(m_SndEID, *i, &no_events); } } @@ -1401,7 +1401,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // as redundant links at the connecting stage and became // writable (connected) before this function had a chance // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_CONNECT); + m_Global.m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_CONNECT); } } @@ -1446,7 +1446,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { { InvertedLock ung (m_GroupLock); - enterCS(CUDT::s_UDTUnited.m_GlobControlLock); + enterCS(CUDT::uglobal().m_GlobControlLock); HLOGC(gslog.Debug, log << "grp/sendBroadcast: Locked GlobControlLock, locking back GroupLock"); } @@ -1454,7 +1454,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // the Sendstate::it field shall not be used here! for (vector::iterator is = sendstates.begin(); is != sendstates.end(); ++is) { - CUDTSocket* ps = CUDT::s_UDTUnited.locateSocket_LOCKED(is->id); + CUDTSocket* ps = CUDT::uglobal().locateSocket_LOCKED(is->id); // Is the socket valid? If not, simply SKIP IT. Nothing to be done with it, // it's already deleted. @@ -1498,7 +1498,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Now you can leave GlobControlLock, while GroupLock is still locked. - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); } // Re-check after the waiting lock has been reacquired @@ -1535,7 +1535,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (was_blocked) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); if (!m_bSynSending) { throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); @@ -1554,7 +1554,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { HLOGC(gslog.Debug, log << "Will block on blocked socket @" << (*b)->id << " as only blocked socket remained"); - CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_SndEID, (*b)->ps, &modes); + CUDT::uglobal().epoll_add_usock_INTERNAL(m_SndEID, (*b)->ps, &modes); } const int blocklen = blocked.size(); @@ -1569,7 +1569,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // m_iSndTimeOut is -1 by default, which matches the meaning of waiting forever THREAD_PAUSED(); - blst = m_pGlobal->m_EPoll.swait(*m_SndEpolld, sready, m_iSndTimeOut); + blst = m_Global.m_EPoll.swait(*m_SndEpolld, sready, m_iSndTimeOut); THREAD_RESUMED(); // NOTE EXCEPTIONS: @@ -1671,8 +1671,8 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (none_succeeded) { HLOGC(gslog.Debug, log << "grp/sendBroadcast: all links broken (none succeeded to send a payload)"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // Reparse error code, if set. // It might be set, if the last operation was failed. // If any operation succeeded, this will not be executed anyway. @@ -1725,7 +1725,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (!ready_again) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); } return rstat; @@ -1871,7 +1871,7 @@ void CUDTGroup::fillGroupData(SRT_MSGCTRL& w_out, // MSGCTRL to be written w_out.grpdata = grpdata; } -// [[using locked(CUDT::s_UDTUnited.m_GlobControLock)]] +// [[using locked(CUDT::uglobal()->m_GlobControLock)]] // [[using locked(m_GroupLock)]] struct FLookupSocketWithEvent_LOCKED { @@ -1978,7 +1978,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // which requires lock on m_GlobControlLock, while this lock cannot be applied without // first unlocking m_GroupLock. const int read_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; - CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); + CUDT::uglobal().epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); } // Here we need to make an additional check. @@ -2014,11 +2014,11 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // This call may wait indefinite time, so GroupLock must be unlocked. InvertedLock ung (m_GroupLock); THREAD_PAUSED(); - nready = m_pGlobal->m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); + nready = m_Global.m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); THREAD_RESUMED(); // HERE GlobControlLock is locked first, then GroupLock is applied back - enterCS(CUDT::s_UDTUnited.m_GlobControlLock); + enterCS(CUDT::uglobal().m_GlobControlLock); } // BOTH m_GlobControlLock AND m_GroupLock are locked here. @@ -2028,7 +2028,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& { // GlobControlLock is applied manually, so unlock manually. // GroupLock will be unlocked as per scope. - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); // This can only happen when 0 is passed as timeout and none is ready. // And 0 is passed only in non-blocking mode. So this is none ready in // non-blocking mode. @@ -2049,7 +2049,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& /*FROM*/ sready.begin(), sready.end(), /*TO*/ std::inserter(w_broken, w_broken.begin()), - /*VIA*/ FLookupSocketWithEvent_LOCKED(m_pGlobal, SRT_EPOLL_ERR)); + /*VIA*/ FLookupSocketWithEvent_LOCKED(&m_Global, SRT_EPOLL_ERR)); // If this set is empty, it won't roll even once, therefore output @@ -2081,7 +2081,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& } } - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); return readReady; } @@ -2132,7 +2132,7 @@ void CUDTGroup::updateReadState(SRTSOCKET /* not sure if needed */, int32_t sequ if (ready) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); } } @@ -2145,7 +2145,7 @@ int32_t CUDTGroup::getRcvBaseSeqNo() void CUDTGroup::updateWriteState() { ScopedLock lg(m_GroupLock); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); } /// Validate iPktSeqno is in range @@ -2199,7 +2199,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) size_t output_size = 0; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) @@ -2209,13 +2209,13 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // must fist wait for being able to acquire this lock. // The group will not be deleted now because it is added usage counter // by this call, but will be released once it exits. - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } // Now, still under lock, check if all sockets still can be dispatched send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); if (m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -2254,7 +2254,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // We predict to have only one packet ahead, others are pending to be reported by tsbpd. // This will be "re-enabled" if the later check puts any new packet into ahead. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); return len; } @@ -2543,7 +2543,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) InvertedLock ung (m_GroupLock); for (set::iterator b = broken.begin(); b != broken.end(); ++b) { - CUDT::s_UDTUnited.close(*b); + CUDT::uglobal().close(*b); } } @@ -2551,7 +2551,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // All broken HLOGC(grlog.Debug, log << "group/recv: All sockets broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -2586,7 +2586,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // Don't clear the read-readinsess state if you have a packet ahead because // if you have, the next read call will return it. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); } HLOGC(grlog.Debug, @@ -2697,7 +2697,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // Don't clear the read-readinsess state if you have a packet ahead because // if you have, the next read call will return it. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); } return len; } @@ -3452,7 +3452,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c // at the connecting stage. CEPoll::fmap_t sready; - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); @@ -3461,7 +3461,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c { InvertedLock ug(m_GroupLock); - m_pGlobal->m_EPoll.swait( + m_Global.m_EPoll.swait( *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything has happened } @@ -3472,7 +3472,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c } // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); @@ -3493,7 +3493,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); const int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, sockid, &no_events); + m_Global.m_EPoll.update_usock(m_SndEID, sockid, &no_events); } // After that, all sockets that have been reported @@ -3502,7 +3502,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c // as redundant links at the connecting stage and became // writable (connected) before this function had a chance // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); + m_Global.m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); } // [[using locked(this->m_GroupLock)]] @@ -3560,11 +3560,11 @@ void CUDTGroup::send_CloseBrokenSockets(vector& w_wipeme) // With unlocked GroupLock, we can now lock GlobControlLock. // This is needed prevent any of them be deleted from the container // at the same time. - ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock globlock(CUDT::uglobal().m_GlobControlLock); for (vector::iterator p = w_wipeme.begin(); p != w_wipeme.end(); ++p) { - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(*p); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(*p); // If the socket has been just moved to ClosedSockets, it means that // the object still exists, but it will be no longer findable. @@ -3597,7 +3597,7 @@ void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) // With unlocked GroupLock, we can now lock GlobControlLock. // This is needed prevent any of them be deleted from the container // at the same time. - ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock globlock(CUDT::uglobal().m_GlobControlLock); typedef vector::const_iterator const_iter_t; for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) @@ -3607,7 +3607,7 @@ void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) // m_GroupLock is unlocked, therefore member->pSocketData can't be used. const SRTSOCKET sockid = member->socketID; - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(sockid); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(sockid); // If the socket has been just moved to ClosedSockets, it means that // the object still exists, but it will be no longer findable. @@ -3674,10 +3674,10 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx // Note: GroupLock is set already, skip locks and checks getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // wipeme wiped, pending sockets checked, it can only mean that // all sockets are broken. @@ -3713,7 +3713,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx RetryWaitBlocked: { // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -3723,7 +3723,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx HLOGC(gslog.Debug, log << "grp/sendBackup: swait call to get at least one link alive up to " << m_iSndTimeOut << "us"); THREAD_PAUSED(); - brdy = m_pGlobal->m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); + brdy = m_Global.m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); THREAD_RESUMED(); if (brdy == 0) // SND timeout exceeded @@ -3741,13 +3741,13 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx if (i->second & SRT_EPOLL_ERR) { SRTSOCKET id = i->first; - CUDTSocket* s = m_pGlobal->locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! + CUDTSocket* s = m_Global.locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! if (s) { HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " while waiting for any writable socket - CLOSING"); - CUDT::s_UDTUnited.close(s); // << LOCKS m_GlobControlLock, then GroupLock! + CUDT::uglobal().close(s); // << LOCKS m_GlobControlLock, then GroupLock! } else { @@ -3771,8 +3771,8 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx LOGC(gslog.Error, log << "grp/sendBackup: swait=>" << brdy << " nlinks=" << nlinks << " ndead=" << ndead << " - looxlike all links broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // You can safely throw here - nothing to fill in when all sockets down. // (timeout was reported by exception in the swait call). throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -3939,18 +3939,18 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // [[using assert(this->m_pSndBuffer != nullptr)]]; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) { - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } // Now, still under lock, check if all sockets still can be dispatched send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); steady_clock::time_point currtime = steady_clock::now(); @@ -4011,8 +4011,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (none_succeeded) { HLOGC(gslog.Debug, log << "grp/sendBackup: all links broken (none succeeded to send a payload)"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // Reparse error code, if set. // It might be set, if the last operation was failed. // If any operation succeeded, this will not be executed anyway. @@ -4062,7 +4062,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (!ready_again) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); } HLOGC(gslog.Debug, @@ -4416,7 +4416,7 @@ void CUDTGroup::setGroupConnected() if (!m_bConnected) { // Switch to connected state and give appropriate signal - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); m_bConnected = true; } } @@ -4493,19 +4493,19 @@ void CUDTGroup::activateUpdateEvent(bool still_have_items) // was deleted from the group. This might make the group empty. if (!still_have_items) // empty, or removal of unknown socket attempted - set error on group { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); } else { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_UPDATE, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_UPDATE, true); } } void CUDTGroup::addEPoll(int eid) { - enterCS(m_pGlobal->m_EPoll.m_EPollLock); + enterCS(m_Global.m_EPoll.m_EPollLock); m_sPollID.insert(eid); - leaveCS(m_pGlobal->m_EPoll.m_EPollLock); + leaveCS(m_Global.m_EPoll.m_EPollLock); bool any_read = false; bool any_write = false; @@ -4543,14 +4543,14 @@ void CUDTGroup::addEPoll(int eid) // because we know it is, as we just added it. But it's not performance // critical, sockets are not being often added during transmission. if (any_read) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); if (any_write) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); // Set broken if none is non-broken (pending, read-ready or write-ready) if (any_broken && !any_pending) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); } void CUDTGroup::removeEPollEvents(const int eid) @@ -4559,14 +4559,14 @@ void CUDTGroup::removeEPollEvents(const int eid) // since this happens after the epoll ID has been removed, they cannot be set again set remove; remove.insert(eid); - m_pGlobal->m_EPoll.update_events(id(), remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } void CUDTGroup::removeEPollID(const int eid) { - enterCS(m_pGlobal->m_EPoll.m_EPollLock); + enterCS(m_Global.m_EPoll.m_EPollLock); m_sPollID.erase(eid); - leaveCS(m_pGlobal->m_EPoll.m_EPollLock); + leaveCS(m_Global.m_EPoll.m_EPollLock); } void CUDTGroup::updateFailedLink() @@ -4588,7 +4588,7 @@ void CUDTGroup::updateFailedLink() { // No healthy links, set ERR on epoll. HLOGC(gmlog.Debug, log << "group/updateFailedLink: All sockets broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); } else { @@ -4597,7 +4597,7 @@ void CUDTGroup::updateFailedLink() } #if ENABLE_HEAVY_LOGGING -// [[using maybe_locked(CUDT::s_UDTUnited.m_GlobControlLock)]] +// [[using maybe_locked(CUDT::uglobal()->m_GlobControlLock)]] void CUDTGroup::debugGroup() { ScopedLock gg(m_GroupLock); diff --git a/srtcore/group.h b/srtcore/group.h index 04c8e7e01..9927a2f6d 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -395,7 +395,7 @@ class CUDTGroup // If so, grab the status of all member sockets. void getGroupCount(size_t& w_size, bool& w_still_alive); - class srt::CUDTUnited* m_pGlobal; + srt::CUDTUnited& m_Global; srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; @@ -655,7 +655,7 @@ class CUDTGroup void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); /// The function polls alive member sockets and retrieves a list of read-ready. - /// [acquires lock for CUDT::s_UDTUnited.m_GlobControlLock] + /// [acquires lock for CUDT::uglobal()->m_GlobControlLock] /// [[using locked(m_GroupLock)]] temporally unlocks-locks internally /// /// @returns list of read-ready sockets diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 03ba97e47..62d158af7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1004,7 +1004,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // be normally closed by the application, after it is done with them. // app can call any UDT API to learn the connection_broken error - CUDT::s_UDTUnited.m_EPoll.update_events( + CUDT::uglobal().m_EPoll.update_events( i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); i->u->completeBrokenConnectionDependencies(i->errorcode); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 1cfdf8ff9..46afd8981 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -211,7 +211,7 @@ TEST(TestFEC, ConfigExchange) CUDTSocket* s1; - SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1); TestMockCUDT m1; m1.core = &s1->core(); @@ -243,7 +243,7 @@ TEST(TestFEC, ConfigExchangeFaux) CUDTSocket* s1; - SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1); const char* fec_config_wrong [] = { "FEC,Cols:20", // D: unknown filter From 790b7831fb7ec4851111e38ce601e4a13548847c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 16:18:26 +0200 Subject: [PATCH 278/790] [core] Small refax of CUDTUnited::channelSettingsMatch(..) --- srtcore/api.cpp | 127 ++++++++++++++++++++++++------------------------ srtcore/api.h | 10 +++- srtcore/core.h | 7 ++- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 38b86cf8c..dd8266f5c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1816,71 +1816,69 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) { - ScopedLock cg(s->m_ControlLock); - // a socket can "connect" only if it is in the following states: - // - OPENED: assume the socket binding parameters are configured - // - INIT: configure binding parameters here - // - any other (meaning, already connected): report error - - if (s->m_Status == SRTS_INIT) - { - if (s->core().m_config.bRendezvous) - throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); - - // If bind() was done first on this socket, then the - // socket will not perform this step. This actually does the - // same thing as bind() does, just with empty address so that - // the binding parameters are autoselected. - - s->core().open(); - sockaddr_any autoselect_sa (target_addr.family()); - // This will create such a sockaddr_any that - // will return true from empty(). - updateMux(s, autoselect_sa); // <<---- updateMux - // -> C(Snd|Rcv)Queue::init - // -> pthread_create(...C(Snd|Rcv)Queue::worker...) - s->m_Status = SRTS_OPENED; - } - else - { - if (s->m_Status != SRTS_OPENED) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + ScopedLock cg(s->m_ControlLock); + // a socket can "connect" only if it is in the following states: + // - OPENED: assume the socket binding parameters are configured + // - INIT: configure binding parameters here + // - any other (meaning, already connected): report error - // status = SRTS_OPENED, so family should be known already. - if (target_addr.family() != s->m_SelfAddr.family()) - { - LOGP(cnlog.Error, "srt_connect: socket is bound to a different family than target address"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } + if (s->m_Status == SRTS_INIT) + { + if (s->core().m_config.bRendezvous) + throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); + + // If bind() was done first on this socket, then the + // socket will not perform this step. This actually does the + // same thing as bind() does, just with empty address so that + // the binding parameters are autoselected. + + s->core().open(); + sockaddr_any autoselect_sa (target_addr.family()); + // This will create such a sockaddr_any that + // will return true from empty(). + updateMux(s, autoselect_sa); // <<---- updateMux + // -> C(Snd|Rcv)Queue::init + // -> pthread_create(...C(Snd|Rcv)Queue::worker...) + s->m_Status = SRTS_OPENED; + } + else + { + if (s->m_Status != SRTS_OPENED) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + // status = SRTS_OPENED, so family should be known already. + if (target_addr.family() != s->m_SelfAddr.family()) + { + LOGP(cnlog.Error, "srt_connect: socket is bound to a different family than target address"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } - // connect_complete() may be called before connect() returns. - // So we need to update the status before connect() is called, - // otherwise the status may be overwritten with wrong value - // (CONNECTED vs. CONNECTING). - s->m_Status = SRTS_CONNECTING; - /* - * In blocking mode, connect can block for up to 30 seconds for - * rendez-vous mode. Holding the s->m_ControlLock prevent close - * from cancelling the connect - */ - try - { - // record peer address - s->m_PeerAddr = target_addr; - s->core().startConnect(target_addr, forced_isn); - } - catch (CUDTException& e) // Interceptor, just to change the state. - { - s->m_Status = SRTS_OPENED; - throw e; - } + // connect_complete() may be called before connect() returns. + // So we need to update the status before connect() is called, + // otherwise the status may be overwritten with wrong value + // (CONNECTED vs. CONNECTING). + s->m_Status = SRTS_CONNECTING; - // ScopedLock destructor will delete cg and unlock s->m_ControlLock + /* + * In blocking mode, connect can block for up to 30 seconds for + * rendez-vous mode. Holding the s->m_ControlLock prevent close + * from cancelling the connect + */ + try + { + // record peer address + s->m_PeerAddr = target_addr; + s->core().startConnect(target_addr, forced_isn); + } + catch (CUDTException& e) // Interceptor, just to change the state. + { + s->m_Status = SRTS_OPENED; + throw e; + } - return 0; + return 0; } @@ -2857,9 +2855,9 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) return sa.hport(); } -bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) +bool srt::CUDTUnited::channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket) { - return m.m_mcfg.bReuseAddr && m.m_mcfg == s->core().m_config; + return cfgMuxer.bReuseAddr && cfgMuxer == cfgSocket; } void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) @@ -2876,6 +2874,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // If not, we need to see if there exist already a multiplexer bound // to the same endpoint. const int port = addr.hport(); + const CSrtConfig& cfgSocket = s->core().m_config; bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); @@ -2912,14 +2911,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // Still, for ANY you need either the same family, or open // for families. - if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->core().m_config.iIpV6Only) + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != cfgSocket.iIpV6Only) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with existing IPv6 wildcard binding: " << sa.str()); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } - if ((m.m_mcfg.iIpV6Only == 0 || s->core().m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + if ((m.m_mcfg.iIpV6Only == 0 || cfgSocket.iIpV6Only == 0) && m.m_iIPversion != addr.family()) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str() @@ -2955,7 +2954,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U if (reuse_attempt) { // - if the channel settings match, it can be reused - if (channelSettingsMatch(m, s)) + if (channelSettingsMatch(m.m_mcfg, cfgSocket)) { HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port); // reuse the existing multiplexer diff --git a/srtcore/api.h b/srtcore/api.h index a1bfbffd7..516322292 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,7 +123,8 @@ class CUDTSocket void construct(); - srt::sync::atomic m_Status; //< current socket state + SRT_ATTR_GUARDED_BY(m_ControlLock) + sync::atomic m_Status; //< current socket state /// Time when the socket is closed. /// When the socket is closed, it is not removed immediately from the list @@ -441,7 +442,12 @@ friend class CRendezvousQueue; // Utility functions for updateMux void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm); - bool channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s); + + /// @brief Checks if channel configuration matches the socket configuration. + /// @param cfgMuxer multiplexer configuration. + /// @param cfgSocket socket configuration. + /// @return tru if configurations match, false otherwise. + static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket); private: std::map m_mMultiplexer; // UDP multiplexer diff --git a/srtcore/core.h b/srtcore/core.h index 1fd5f6730..e74c58c6e 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -499,14 +499,17 @@ class CUDT SRT_ATR_NODISCARD size_t fillSrtHandshake_HSRSP(uint32_t* srtdata, size_t srtlen, int hs_version); SRT_ATR_NODISCARD size_t fillSrtHandshake(uint32_t* srtdata, size_t srtlen, int msgtype, int hs_version); - SRT_ATR_NODISCARD bool createSrtHandshake(int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen, + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + bool createSrtHandshake(int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen, CPacket& w_reqpkt, CHandShake& w_hs); SRT_ATR_NODISCARD size_t fillHsExtConfigString(uint32_t *pcmdspec, int cmd, const std::string &str); #if ENABLE_EXPERIMENTAL_BONDING SRT_ATR_NODISCARD size_t fillHsExtGroup(uint32_t *pcmdspec); #endif - SRT_ATR_NODISCARD size_t fillHsExtKMREQ(uint32_t *pcmdspec, size_t ki); + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + size_t fillHsExtKMREQ(uint32_t *pcmdspec, size_t ki); + SRT_ATR_NODISCARD size_t fillHsExtKMRSP(uint32_t *pcmdspec, const uint32_t *kmdata, size_t kmdata_wordsize); SRT_ATR_NODISCARD size_t prepareSrtHsMsg(int cmd, uint32_t* srtdata, size_t size); From 2031b2c696f5f1bbffddf54b24c747c0db7d0a63 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 16:20:16 +0200 Subject: [PATCH 279/790] [core] Do not set peerAddress in connectIn(..). CUDT::startConnect(..) sets it itself. --- srtcore/api.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index dd8266f5c..e35d637a5 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1868,8 +1868,6 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i */ try { - // record peer address - s->m_PeerAddr = target_addr; s->core().startConnect(target_addr, forced_isn); } catch (CUDTException& e) // Interceptor, just to change the state. From ce2742b9ce6546a5252367b23c63404bfbfeb7f1 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 5 Oct 2021 10:55:47 +0200 Subject: [PATCH 280/790] [apps] Split off stats writer from apputil.cpp (#2130) --- apps/apputil.cpp | 334 +---------------------------------- apps/apputil.hpp | 87 ---------- apps/statswriter.cpp | 355 ++++++++++++++++++++++++++++++++++++++ apps/statswriter.hpp | 107 ++++++++++++ apps/support.maf | 1 + apps/transmitbase.hpp | 1 + testing/srt-test-live.maf | 1 + testing/testmedia.hpp | 1 + 8 files changed, 467 insertions(+), 420 deletions(-) create mode 100644 apps/statswriter.cpp create mode 100644 apps/statswriter.hpp diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 1389b748e..68a9c4670 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -16,6 +16,7 @@ #include #include +#include "srt.h" // Required for SRT_SYNC_CLOCK_* definitions. #include "apputil.hpp" #include "netinet_any.h" #include "srt_compat.h" @@ -352,339 +353,6 @@ string OptionHelpItem(const OptionName& o) return out; } -// Stats module - -// Note: std::put_time is supported only in GCC 5 and higher -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) -#define HAS_PUT_TIME -#endif - -template -inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, - TYPE CBytePerfMon::*field) -{ - return new SrtStatDataType(cat, name, longname, field); -} - -#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field)) -#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field) - -vector> g_SrtStatsTable; - -struct SrtStatsTableInit -{ - SrtStatsTableInit(vector>& s) - { - STATX(GEN, time, Time, msTimeStamp); - - STAT(WINDOW, flow, pktFlowWindow); - STAT(WINDOW, congestion, pktCongestionWindow); - STAT(WINDOW, flight, pktFlightSize); - - STAT(LINK, rtt, msRTT); - STAT(LINK, bandwidth, mbpsBandwidth); - STAT(LINK, maxBandwidth, mbpsMaxBW); - - STAT(SEND, packets, pktSent); - STAT(SEND, packetsUnique, pktSentUnique); - STAT(SEND, packetsLost, pktSndLoss); - STAT(SEND, packetsDropped, pktSndDrop); - STAT(SEND, packetsRetransmitted, pktRetrans); - STAT(SEND, packetsFilterExtra, pktSndFilterExtra); - STAT(SEND, bytes, byteSent); - STAT(SEND, bytesUnique, byteSentUnique); - STAT(SEND, bytesDropped, byteSndDrop); - STAT(SEND, byteAvailBuf, byteAvailSndBuf); - STAT(SEND, msBuf, msSndBuf); - STAT(SEND, mbitRate, mbpsSendRate); - STAT(SEND, sendPeriod, usPktSndPeriod); - - STAT(RECV, packets, pktRecv); - STAT(RECV, packetsUnique, pktRecvUnique); - STAT(RECV, packetsLost, pktRcvLoss); - STAT(RECV, packetsDropped, pktRcvDrop); - STAT(RECV, packetsRetransmitted, pktRcvRetrans); - STAT(RECV, packetsBelated, pktRcvBelated); - STAT(RECV, packetsFilterExtra, pktRcvFilterExtra); - STAT(RECV, packetsFilterSupply, pktRcvFilterSupply); - STAT(RECV, packetsFilterLoss, pktRcvFilterLoss); - STAT(RECV, bytes, byteRecv); - STAT(RECV, bytesUnique, byteRecvUnique); - STAT(RECV, bytesLost, byteRcvLoss); - STAT(RECV, bytesDropped, byteRcvDrop); - STAT(RECV, byteAvailBuf, byteAvailRcvBuf); - STAT(RECV, msBuf, msRcvBuf); - STAT(RECV, mbitRate, mbpsRecvRate); - STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); - } -} g_SrtStatsTableInit (g_SrtStatsTable); - - -#undef STAT -#undef STATX - -string srt_json_cat_names [] = { - "", - "window", - "link", - "send", - "recv" -}; - -#ifdef HAS_PUT_TIME -// Follows ISO 8601 -std::string SrtStatsWriter::print_timestamp() -{ - using namespace std; - using namespace std::chrono; - - const auto systime_now = system_clock::now(); - const time_t time_now = system_clock::to_time_t(systime_now); - - std::ostringstream output; - - // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. - const tm tm_now = SysLocalTime(time_now); - output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); - const auto since_epoch = systime_now.time_since_epoch(); - const seconds s = duration_cast(since_epoch); - output << duration_cast(since_epoch - s).count(); - output << std::put_time(&tm_now, "%z"); - return output.str(); -} -#else - -// This is a stub. The error when not defining it would be too -// misleading, so this stub will work if someone mistakenly adds -// the item to the output format without checking that HAS_PUT_TIME. -string SrtStatsWriter::print_timestamp() -{ return ""; } -#endif // HAS_PUT_TIME - - -class SrtStatsJson : public SrtStatsWriter -{ - static string quotekey(const string& name) - { - if (name == "") - return ""; - - return R"(")" + name + R"(":)"; - } - - static string quote(const string& name) - { - if (name == "") - return ""; - - return R"(")" + name + R"(")"; - } - -public: - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - - string pretty_cr, pretty_tab; - if (Option("pretty")) - { - pretty_cr = "\n"; - pretty_tab = "\t"; - } - - SrtStatCat cat = SSC_GEN; - - // Do general manually - output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr; - - // SID is displayed manually - output << pretty_tab << quotekey("sid") << sid; - - // Extra Timepoint is also displayed manually -#ifdef HAS_PUT_TIME - // NOTE: still assumed SSC_GEN category - output << "," << pretty_cr << pretty_tab - << quotekey("timepoint") << quote(print_timestamp()); -#endif - - // Now continue with fields as specified in the table - for (auto& i: g_SrtStatsTable) - { - if (i->category == cat) - { - output << ","; // next item in same cat - output << pretty_cr; - output << pretty_tab; - if (cat != SSC_GEN) - output << pretty_tab; - } - else - { - if (cat != SSC_GEN) - { - // DO NOT close if general category, just - // enter the depth. - output << pretty_cr << pretty_tab << "}"; - } - cat = i->category; - output << ","; - output << pretty_cr; - if (cat != SSC_GEN) - output << pretty_tab; - - output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; - if (cat != SSC_GEN) - output << pretty_tab; - } - - // Print the current field - output << quotekey(i->name); - i->PrintValue(output, mon); - } - - // Close the previous subcategory - if (cat != SSC_GEN) - { - output << pretty_cr << pretty_tab << "}" << pretty_cr; - } - - // Close the general category entity - output << "}" << pretty_cr << endl; - - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl; - return output.str(); - } -}; - -class SrtStatsCsv : public SrtStatsWriter -{ -private: - bool first_line_printed; - -public: - SrtStatsCsv() : first_line_printed(false) {} - - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - - // Header - if (!first_line_printed) - { -#ifdef HAS_PUT_TIME - output << "Timepoint,"; -#endif - output << "Time,SocketID"; - - for (auto& i: g_SrtStatsTable) - { - output << "," << i->longname; - } - output << endl; - first_line_printed = true; - } - - // Values -#ifdef HAS_PUT_TIME - // HDR: Timepoint - output << print_timestamp() << ","; -#endif // HAS_PUT_TIME - - // HDR: Time,SocketID - output << mon.msTimeStamp << "," << sid; - - // HDR: the loop of all values in g_SrtStatsTable - for (auto& i: g_SrtStatsTable) - { - output << ","; - i->PrintValue(output, mon); - } - - output << endl; - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; - return output.str(); - } -}; - -class SrtStatsCols : public SrtStatsWriter -{ -public: - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - output << "======= SRT STATS: sid=" << sid << endl; - output << "PACKETS SENT: " << setw(11) << mon.pktSent << " RECEIVED: " << setw(11) << mon.pktRecv << endl; - output << "LOST PKT SENT: " << setw(11) << mon.pktSndLoss << " RECEIVED: " << setw(11) << mon.pktRcvLoss << endl; - output << "REXMIT SENT: " << setw(11) << mon.pktRetrans << " RECEIVED: " << setw(11) << mon.pktRcvRetrans << endl; - output << "DROP PKT SENT: " << setw(11) << mon.pktSndDrop << " RECEIVED: " << setw(11) << mon.pktRcvDrop << endl; - output << "FILTER EXTRA TX: " << setw(11) << mon.pktSndFilterExtra << " RX: " << setw(11) << mon.pktRcvFilterExtra << endl; - output << "FILTER RX SUPPL: " << setw(11) << mon.pktRcvFilterSupply << " RX LOSS: " << setw(11) << mon.pktRcvFilterLoss << endl; - output << "RATE SENDING: " << setw(11) << mon.mbpsSendRate << " RECEIVING: " << setw(11) << mon.mbpsRecvRate << endl; - output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl; - output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl; - output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl; - output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; - output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl; - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; - return output.str(); - } -}; - -shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat) -{ - switch (printformat) - { - case SRTSTATS_PROFMAT_JSON: - return make_shared(); - case SRTSTATS_PROFMAT_CSV: - return make_shared(); - case SRTSTATS_PROFMAT_2COLS: - return make_shared(); - default: - break; - } - return nullptr; -} - -SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) -{ - size_t havecomma = pf.find(','); - if (havecomma != string::npos) - { - w_extras = pf.substr(havecomma+1); - pf = pf.substr(0, havecomma); - } - - if (pf == "default") - return SRTSTATS_PROFMAT_2COLS; - - if (pf == "json") - return SRTSTATS_PROFMAT_JSON; - - if (pf == "csv") - return SRTSTATS_PROFMAT_CSV; - - return SRTSTATS_PROFMAT_INVALID; -} - const char* SRTClockTypeStr() { const int clock_type = srt_clock_type(); diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 782412815..7737c05f7 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -77,8 +77,6 @@ inline void SysCleanupNetwork() {} #endif -#include "srt.h" // Required for stats module - #ifdef _WIN32 inline int SysError() { return ::GetLastError(); } const int SysAGAIN = WSAEWOULDBLOCK; @@ -335,92 +333,7 @@ inline bool OptionPresent(const options_t& options, const std::set& options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); std::string OptionHelpItem(const OptionName& o); -// Statistics module - -enum SrtStatsPrintFormat -{ - SRTSTATS_PROFMAT_INVALID = -1, - SRTSTATS_PROFMAT_2COLS = 0, - SRTSTATS_PROFMAT_JSON, - SRTSTATS_PROFMAT_CSV -}; - -SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras); - -enum SrtStatCat -{ - SSC_GEN, //< General - SSC_WINDOW, // flow/congestion window - SSC_LINK, //< Link data - SSC_SEND, //< Sending - SSC_RECV //< Receiving -}; - -struct SrtStatData -{ - SrtStatCat category; - std::string name; - std::string longname; - - SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} - virtual ~SrtStatData() {} - - virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; -}; - -template -struct SrtStatDataType: public SrtStatData -{ - typedef TYPE CBytePerfMon::*pfield_t; - pfield_t pfield; - - SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field) - : SrtStatData (cat, name, longname), pfield(field) - { - } - - void PrintValue(std::ostream& str, const CBytePerfMon& mon) override - { - str << mon.*pfield; - } -}; - -class SrtStatsWriter -{ -public: - virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; - virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; - virtual ~SrtStatsWriter() { }; - - // Only if HAS_PUT_TIME. Specified in the imp file. - std::string print_timestamp(); - - void Option(const std::string& key, const std::string& val) - { - options[key] = val; - } - - bool Option(const std::string& key, std::string* rval = nullptr) - { - const std::string* out = map_getp(options, key); - if (!out) - return false; - - if (rval) - *rval = *out; - return true; - } - -protected: - std::map options; -}; - -extern std::vector> g_SrtStatsTable; - -std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); - const char* SRTClockTypeStr(); void PrintLibVersion(); - #endif // INC_SRT_APPCOMMON_H diff --git a/apps/statswriter.cpp b/apps/statswriter.cpp new file mode 100644 index 000000000..c176248ab --- /dev/null +++ b/apps/statswriter.cpp @@ -0,0 +1,355 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "statswriter.hpp" +#include "netinet_any.h" +#include "srt_compat.h" + +// Note: std::put_time is supported only in GCC 5 and higher +#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) +#define HAS_PUT_TIME +#endif + +using namespace std; + + +template +inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, + TYPE CBytePerfMon::*field) +{ + return new SrtStatDataType(cat, name, longname, field); +} + +#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field)) +#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field) + +vector> g_SrtStatsTable; + +struct SrtStatsTableInit +{ + SrtStatsTableInit(vector>& s) + { + STATX(GEN, time, Time, msTimeStamp); + + STAT(WINDOW, flow, pktFlowWindow); + STAT(WINDOW, congestion, pktCongestionWindow); + STAT(WINDOW, flight, pktFlightSize); + + STAT(LINK, rtt, msRTT); + STAT(LINK, bandwidth, mbpsBandwidth); + STAT(LINK, maxBandwidth, mbpsMaxBW); + + STAT(SEND, packets, pktSent); + STAT(SEND, packetsUnique, pktSentUnique); + STAT(SEND, packetsLost, pktSndLoss); + STAT(SEND, packetsDropped, pktSndDrop); + STAT(SEND, packetsRetransmitted, pktRetrans); + STAT(SEND, packetsFilterExtra, pktSndFilterExtra); + STAT(SEND, bytes, byteSent); + STAT(SEND, bytesUnique, byteSentUnique); + STAT(SEND, bytesDropped, byteSndDrop); + STAT(SEND, byteAvailBuf, byteAvailSndBuf); + STAT(SEND, msBuf, msSndBuf); + STAT(SEND, mbitRate, mbpsSendRate); + STAT(SEND, sendPeriod, usPktSndPeriod); + + STAT(RECV, packets, pktRecv); + STAT(RECV, packetsUnique, pktRecvUnique); + STAT(RECV, packetsLost, pktRcvLoss); + STAT(RECV, packetsDropped, pktRcvDrop); + STAT(RECV, packetsRetransmitted, pktRcvRetrans); + STAT(RECV, packetsBelated, pktRcvBelated); + STAT(RECV, packetsFilterExtra, pktRcvFilterExtra); + STAT(RECV, packetsFilterSupply, pktRcvFilterSupply); + STAT(RECV, packetsFilterLoss, pktRcvFilterLoss); + STAT(RECV, bytes, byteRecv); + STAT(RECV, bytesUnique, byteRecvUnique); + STAT(RECV, bytesLost, byteRcvLoss); + STAT(RECV, bytesDropped, byteRcvDrop); + STAT(RECV, byteAvailBuf, byteAvailRcvBuf); + STAT(RECV, msBuf, msRcvBuf); + STAT(RECV, mbitRate, mbpsRecvRate); + STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); + } +} g_SrtStatsTableInit (g_SrtStatsTable); + + +#undef STAT +#undef STATX + +string srt_json_cat_names [] = { + "", + "window", + "link", + "send", + "recv" +}; + +#ifdef HAS_PUT_TIME +// Follows ISO 8601 +std::string SrtStatsWriter::print_timestamp() +{ + using namespace std; + using namespace std::chrono; + + const auto systime_now = system_clock::now(); + const time_t time_now = system_clock::to_time_t(systime_now); + + std::ostringstream output; + + // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. + const tm tm_now = SysLocalTime(time_now); + output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); + const auto since_epoch = systime_now.time_since_epoch(); + const seconds s = duration_cast(since_epoch); + output << duration_cast(since_epoch - s).count(); + output << std::put_time(&tm_now, "%z"); + return output.str(); +} +#else + +// This is a stub. The error when not defining it would be too +// misleading, so this stub will work if someone mistakenly adds +// the item to the output format without checking that HAS_PUT_TIME. +string SrtStatsWriter::print_timestamp() +{ return ""; } +#endif // HAS_PUT_TIME + + +class SrtStatsJson : public SrtStatsWriter +{ + static string quotekey(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(":)"; + } + + static string quote(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(")"; + } + +public: + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + + string pretty_cr, pretty_tab; + if (Option("pretty")) + { + pretty_cr = "\n"; + pretty_tab = "\t"; + } + + SrtStatCat cat = SSC_GEN; + + // Do general manually + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr; + + // SID is displayed manually + output << pretty_tab << quotekey("sid") << sid; + + // Extra Timepoint is also displayed manually +#ifdef HAS_PUT_TIME + // NOTE: still assumed SSC_GEN category + output << "," << pretty_cr << pretty_tab + << quotekey("timepoint") << quote(print_timestamp()); +#endif + + // Now continue with fields as specified in the table + for (auto& i: g_SrtStatsTable) + { + if (i->category == cat) + { + output << ","; // next item in same cat + output << pretty_cr; + output << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + else + { + if (cat != SSC_GEN) + { + // DO NOT close if general category, just + // enter the depth. + output << pretty_cr << pretty_tab << "}"; + } + cat = i->category; + output << ","; + output << pretty_cr; + if (cat != SSC_GEN) + output << pretty_tab; + + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + + // Print the current field + output << quotekey(i->name); + i->PrintValue(output, mon); + } + + // Close the previous subcategory + if (cat != SSC_GEN) + { + output << pretty_cr << pretty_tab << "}" << pretty_cr; + } + + // Close the general category entity + output << "}" << pretty_cr << endl; + + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl; + return output.str(); + } +}; + +class SrtStatsCsv : public SrtStatsWriter +{ +private: + bool first_line_printed; + +public: + SrtStatsCsv() : first_line_printed(false) {} + + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + + // Header + if (!first_line_printed) + { +#ifdef HAS_PUT_TIME + output << "Timepoint,"; +#endif + output << "Time,SocketID"; + + for (auto& i: g_SrtStatsTable) + { + output << "," << i->longname; + } + output << endl; + first_line_printed = true; + } + + // Values +#ifdef HAS_PUT_TIME + // HDR: Timepoint + output << print_timestamp() << ","; +#endif // HAS_PUT_TIME + + // HDR: Time,SocketID + output << mon.msTimeStamp << "," << sid; + + // HDR: the loop of all values in g_SrtStatsTable + for (auto& i: g_SrtStatsTable) + { + output << ","; + i->PrintValue(output, mon); + } + + output << endl; + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; + return output.str(); + } +}; + +class SrtStatsCols : public SrtStatsWriter +{ +public: + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + output << "======= SRT STATS: sid=" << sid << endl; + output << "PACKETS SENT: " << setw(11) << mon.pktSent << " RECEIVED: " << setw(11) << mon.pktRecv << endl; + output << "LOST PKT SENT: " << setw(11) << mon.pktSndLoss << " RECEIVED: " << setw(11) << mon.pktRcvLoss << endl; + output << "REXMIT SENT: " << setw(11) << mon.pktRetrans << " RECEIVED: " << setw(11) << mon.pktRcvRetrans << endl; + output << "DROP PKT SENT: " << setw(11) << mon.pktSndDrop << " RECEIVED: " << setw(11) << mon.pktRcvDrop << endl; + output << "FILTER EXTRA TX: " << setw(11) << mon.pktSndFilterExtra << " RX: " << setw(11) << mon.pktRcvFilterExtra << endl; + output << "FILTER RX SUPPL: " << setw(11) << mon.pktRcvFilterSupply << " RX LOSS: " << setw(11) << mon.pktRcvFilterLoss << endl; + output << "RATE SENDING: " << setw(11) << mon.mbpsSendRate << " RECEIVING: " << setw(11) << mon.mbpsRecvRate << endl; + output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl; + output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl; + output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl; + output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; + output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl; + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; + return output.str(); + } +}; + +shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat) +{ + switch (printformat) + { + case SRTSTATS_PROFMAT_JSON: + return make_shared(); + case SRTSTATS_PROFMAT_CSV: + return make_shared(); + case SRTSTATS_PROFMAT_2COLS: + return make_shared(); + default: + break; + } + return nullptr; +} + +SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) +{ + size_t havecomma = pf.find(','); + if (havecomma != string::npos) + { + w_extras = pf.substr(havecomma+1); + pf = pf.substr(0, havecomma); + } + + if (pf == "default") + return SRTSTATS_PROFMAT_2COLS; + + if (pf == "json") + return SRTSTATS_PROFMAT_JSON; + + if (pf == "csv") + return SRTSTATS_PROFMAT_CSV; + + return SRTSTATS_PROFMAT_INVALID; +} diff --git a/apps/statswriter.hpp b/apps/statswriter.hpp new file mode 100644 index 000000000..e15b902b7 --- /dev/null +++ b/apps/statswriter.hpp @@ -0,0 +1,107 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + +#ifndef INC_SRT_APPS_STATSWRITER_H +#define INC_SRT_APPS_STATSWRITER_H + +#include +#include +#include +#include + +#include "srt.h" +#include "utilities.h" + +enum SrtStatsPrintFormat +{ + SRTSTATS_PROFMAT_INVALID = -1, + SRTSTATS_PROFMAT_2COLS = 0, + SRTSTATS_PROFMAT_JSON, + SRTSTATS_PROFMAT_CSV +}; + +SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras); + +enum SrtStatCat +{ + SSC_GEN, //< General + SSC_WINDOW, // flow/congestion window + SSC_LINK, //< Link data + SSC_SEND, //< Sending + SSC_RECV //< Receiving +}; + +struct SrtStatData +{ + SrtStatCat category; + std::string name; + std::string longname; + + SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} + virtual ~SrtStatData() {} + + virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; +}; + +template +struct SrtStatDataType: public SrtStatData +{ + typedef TYPE CBytePerfMon::*pfield_t; + pfield_t pfield; + + SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field) + : SrtStatData (cat, name, longname), pfield(field) + { + } + + void PrintValue(std::ostream& str, const CBytePerfMon& mon) override + { + str << mon.*pfield; + } +}; + +class SrtStatsWriter +{ +public: + virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; + virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; + virtual ~SrtStatsWriter() { }; + + // Only if HAS_PUT_TIME. Specified in the imp file. + std::string print_timestamp(); + + void Option(const std::string& key, const std::string& val) + { + options[key] = val; + } + + bool Option(const std::string& key, std::string* rval = nullptr) + { + const std::string* out = map_getp(options, key); + if (!out) + return false; + + if (rval) + *rval = *out; + return true; + } + +protected: + std::map options; +}; + +extern std::vector> g_SrtStatsTable; + +std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); + + + +#endif diff --git a/apps/support.maf b/apps/support.maf index 19ac894b4..e5aa77b8e 100644 --- a/apps/support.maf +++ b/apps/support.maf @@ -9,6 +9,7 @@ SOURCES apputil.cpp +statswriter.cpp logsupport.cpp logsupport_appdefs.cpp socketoptions.cpp diff --git a/apps/transmitbase.hpp b/apps/transmitbase.hpp index 3b3c7d0b9..007f4aaee 100644 --- a/apps/transmitbase.hpp +++ b/apps/transmitbase.hpp @@ -19,6 +19,7 @@ #include "srt.h" #include "uriparser.hpp" #include "apputil.hpp" +#include "statswriter.hpp" typedef std::vector bytevector; extern bool transmit_total_stats; diff --git a/testing/srt-test-live.maf b/testing/srt-test-live.maf index 17368a660..e80d79ec7 100644 --- a/testing/srt-test-live.maf +++ b/testing/srt-test-live.maf @@ -4,6 +4,7 @@ SOURCES srt-test-live.cpp testmedia.cpp ../apps/apputil.cpp +../apps/statswriter.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp ../apps/uriparser.cpp diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index d3447a0d0..ec378080e 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -18,6 +18,7 @@ #include #include "apputil.hpp" +#include "statswriter.hpp" #include "testmediabase.hpp" #include // Needs access to CUDTException #include From 73b49feb9031d78828ac18d412c196e2b3d05b57 Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Wed, 6 Oct 2021 19:44:36 +1000 Subject: [PATCH 281/790] [build] Support NDK r23, OpenSSL v3 (#2148) --- scripts/build-android/build-android | 18 ++++---- scripts/build-android/mkssl | 68 +++++------------------------ 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 0d56a8f01..f2d596847 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -3,16 +3,16 @@ echo_help() { echo "Usage: $0 [options...]" - echo " -n Specify NDK root path for the build." - echo " -a Select target API level." - echo " -t Select target architectures." - echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64." + echo " -n NDK root path for the build" + echo " -a Target API level" + echo " -t Space-separated list of target architectures" + echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64" echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" - echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" - echo " -m Select Mbed TLS version. E.g. v2.26.0" - echo " -s Select SRT version. E.g. v1.4.3" + echo " -o OpenSSL version. E.g. 1.1.1l" + echo " -m Mbed TLS version. E.g. v2.26.0" + echo " -s SRT version. E.g. v1.4.4" echo - echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" + echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\"" echo } @@ -20,7 +20,7 @@ echo_help() NDK_ROOT="" API_LEVEL=28 BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" -OPENSSL_VERSION=1.1.1h +OPENSSL_VERSION=1.1.1l SRT_VERSION="" ENC_LIB=openssl MBEDTLS_VERSION=v2.26.0 diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index 4e9d0df28..ad6547261 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -29,48 +29,22 @@ fi cd openssl-${OPENSSL_VERSION} || exit 128 -##### Prepare Files ##### -sed -i.bak 's/.*-mandroid.*//' Configurations/15-android.conf -patch -p1 -N <{\$lib}. \$shlibvariant. '\$(SHLIB_EXT)'; -+ -+ if (windowsdll()) { -+ return \$lib . '\$(SHLIB_EXT_IMPORT)'; -+ } -+ return \$lib . '\$(SHLIB_EXT_SIMPLE)'; - } -- sub shlib_simple { -+ -+ sub shlib { - my \$lib = shift; - return () if \$disabled{shared} || \$lib =~ /\\.a$/; - -EOP - -##### remove output-directory ##### -#rm -rf $OUT_DIR - ##### export ndk directory. Required by openssl-build-scripts ##### -export ANDROID_NDK +case ${OPENSSL_VERSION} in + 1.1.1*) + export ANDROID_NDK_HOME=$ANDROID_NDK + ;; + *) + export ANDROID_NDK_ROOT=$ANDROID_NDK + ;; +esac + +export PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin:$PATH ##### build-function ##### build_the_thing() { - TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG - export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:"$PATH" -echo $PATH make clean - #./Configure $SSL_TARGET $OPTIONS -fuse-ld="$TOOLCHAIN/$TRIBLE/bin/ld" "-gcc-toolchain $TOOLCHAIN" && \ - ./Configure $SSL_TARGET $OPTIONS -fuse-ld="$TOOLCHAIN/$TRIBLE/bin/ld" && \ + ./Configure $SSL_TARGET -D__ANDROID_API__=$API_LEVEL && \ make && \ make install DESTDIR=$DESTDIR || exit 128 } @@ -79,39 +53,19 @@ echo $PATH for build_target in $BUILD_TARGETS do case $build_target in - armeabi) - TRIBLE="arm-linux-androideabi" - TC_NAME="arm-linux-androideabi-4.9" - #OPTIONS="--target=armv5te-linux-androideabi -mthumb -fPIC -latomic -D__ANDROID_API__=$API_LEVEL" - OPTIONS="--target=armv5te-linux-androideabi -mthumb -fPIC -latomic -D__ANDROID_API__=$API_LEVEL" - DESTDIR="$BUILD_DIR/armeabi" - SSL_TARGET="android-arm" - ;; armeabi-v7a) - TRIBLE="arm-linux-androideabi" - TC_NAME="arm-linux-androideabi-4.9" - OPTIONS="--target=armv7a-linux-androideabi -Wl,--fix-cortex-a8 -fPIC -D__ANDROID_API__=$API_LEVEL" DESTDIR="$BUILD_DIR/armeabi-v7a" SSL_TARGET="android-arm" ;; x86) - TRIBLE="i686-linux-android" - TC_NAME="x86-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/x86" SSL_TARGET="android-x86" ;; x86_64) - TRIBLE="x86_64-linux-android" - TC_NAME="x86_64-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/x86_64" SSL_TARGET="android-x86_64" ;; arm64-v8a) - TRIBLE="aarch64-linux-android" - TC_NAME="aarch64-linux-android-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/arm64-v8a" SSL_TARGET="android-arm64" ;; From 2fb3c9ab06574b042590ff044a5ac06ad278e0b6 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 11 Oct 2021 19:01:45 +0530 Subject: [PATCH 282/790] [api] Use the SOCKET type for any WIN32 environment (#2152) including MinGW. --- srtcore/srt.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/srtcore/srt.h b/srtcore/srt.h index 496112249..4cc2a377f 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -152,11 +152,7 @@ typedef int32_t SRTSOCKET; static const int32_t SRTGROUP_MASK = (1 << 30); #ifdef _WIN32 - #ifndef __MINGW32__ - typedef SOCKET SYSSOCKET; - #else - typedef int SYSSOCKET; - #endif + typedef SOCKET SYSSOCKET; #else typedef int SYSSOCKET; #endif From 6886bfaa6d23ffe74756fdf7ec3b4e7f89ae860d Mon Sep 17 00:00:00 2001 From: hondaxiao Date: Tue, 12 Oct 2021 11:15:20 +0800 Subject: [PATCH 283/790] [build] Add enable-experimental-bonding opt in configure --- configure-data.tcl | 1 + 1 file changed, 1 insertion(+) diff --git a/configure-data.tcl b/configure-data.tcl index 5c0ee2ee7..ab9dfd635 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -54,6 +54,7 @@ set cmake_options { enable-getnameinfo "In-logs sockaddr-to-string should do rev-dns (default: OFF)" enable-unittests "Enable unit tests (default: OFF)" enable-thread-check "Enable #include that implements THREAD_* macros" + enable-experimental-bonding "Enable experimental bonding (default: OFF)" openssl-crypto-library= "Path to a library." openssl-include-dir= "Path to a file." openssl-ssl-library= "Path to a library." From 86d1eb2d98491c71313e887c1bedcecce0932f90 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 5 Oct 2021 17:12:44 +0200 Subject: [PATCH 284/790] [core] Added CUDT::isRcvReady() with mutex lock. Added CUDT::getAvailRcvBufferSize() function with and without mutex lock. --- srtcore/common.h | 2 +- srtcore/core.cpp | 66 ++++++++++++++++++++++++++++++------------------ srtcore/core.h | 28 ++++++++++++++------ srtcore/sync.h | 14 +++++----- 4 files changed, 68 insertions(+), 42 deletions(-) diff --git a/srtcore/common.h b/srtcore/common.h index 530759dde..3e2ce9363 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -616,7 +616,7 @@ class CSeqNo /// This behaves like seq1 - seq2, in comparison to numbers, /// and with the statement that only the sign of the result matters. - /// That is, it returns a negative value if seq1 < seq2, + /// Returns a negative value if seq1 < seq2, /// positive if seq1 > seq2, and zero if they are equal. /// The only correct application of this function is when you /// compare two values and it works faster than seqoff. However diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 53e4a28d5..b3b1d374c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -551,7 +551,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) else { enterCS(m_RecvLock); - if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady()) + if (m_pRcvBuffer && isRcvBufferReady()) event |= SRT_EPOLL_IN; leaveCS(m_RecvLock); if (m_pSndBuffer && (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize())) @@ -5211,7 +5211,6 @@ void * srt::CUDT::tsbpd(void *param) { int32_t skiptoseqno = SRT_SEQNO_NONE; bool passack = true; // Get next packet to wait for even if not acked - rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq), rcv_base_seq); HLOGC(tslog.Debug, @@ -6052,7 +6051,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) UniqueLock recvguard(m_RecvLock); - if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (m_bShutdown) { @@ -6084,7 +6083,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) CSync rcond (m_RecvDataCond, recvguard); CSync tscond (m_RcvTsbPdCond, recvguard); - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { if (!m_config.bSynRecving) { @@ -6096,7 +6095,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (m_config.iRcvTimeOut < 0) { THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) { // Do not block forever, check connection status each 1 sec. rcond.wait_for(seconds_from(1)); @@ -6108,7 +6107,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) const steady_clock::time_point exptime = steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) { if (!rcond.wait_until(exptime)) // NOT means "not received a signal" break; // timeout @@ -6122,7 +6121,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { // See at the beginning if (!m_config.bMessageAPI && m_bShutdown) @@ -6152,7 +6151,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) HLOGP(tslog.Debug, "NOT pinging TSBPD - not set"); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -6607,6 +6606,23 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) return receiveBuffer(data, len); } +size_t srt::CUDT::getAvailRcvBufferSizeLock() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->getAvailBufSize(); +} + +size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const +{ + return m_pRcvBuffer->getAvailBufSize(); +} + +bool srt::CUDT::isRcvBufferReady() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->isRcvDataReady(); +} + // int by_exception: accepts values of CUDTUnited::ErrorHandling: // - 0 - by return value // - 1 - by exception @@ -6647,7 +6663,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality"); enterCS(m_RcvBufferLock); - int res = m_pRcvBuffer->readMsg(data, len); + const int res = m_pRcvBuffer->readMsg(data, len); leaveCS(m_RcvBufferLock); w_mctrl.srctime = 0; @@ -6662,7 +6678,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ HLOGP(tslog.Debug, "NOT pinging TSBPD - not set"); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -6713,7 +6729,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // Kick TsbPd thread to schedule next wakeup (if running) if (m_bTsbPd) @@ -6792,12 +6808,12 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGP(tslog.Debug, "receiveMessage: DATA COND: KICKED."); } - } while (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady())); + } while (stillConnected() && !timeout && (!isRcvBufferReady())); THREAD_RESUMED(); HLOGC(tslog.Debug, log << CONID() << "receiveMessage: lock-waiting loop exited: stillConntected=" << stillConnected() - << " timeout=" << timeout << " data-ready=" << m_pRcvBuffer->isRcvDataReady()); + << " timeout=" << timeout << " data-ready=" << isRcvBufferReady()); } /* XXX DEBUG STUFF - enable when required @@ -6829,7 +6845,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } } while ((res == 0) && !timeout); - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // Falling here means usually that res == 0 && timeout == true. // res == 0 would repeat the above loop, unless there was also a timeout. @@ -6990,7 +7006,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo { if (!m_bConnected || !m_CongCtl.ready()) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + else if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (!m_config.bMessageAPI && m_bShutdown) return 0; @@ -7072,14 +7088,14 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo CSync rcond (m_RecvDataCond, recvguard); THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) rcond.wait(); THREAD_RESUMED(); } if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + else if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (!m_config.bMessageAPI && m_bShutdown) return 0; @@ -7098,7 +7114,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo } } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -7239,7 +7255,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_pRcvBuffer) { - perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_config.iMSS; + perf->byteAvailRcvBuf = getAvailRcvBufferSizeLock() * m_config.iMSS; if (instantaneous) // no need for historical API for Rcv side { perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); @@ -7489,7 +7505,7 @@ void srt::CUDT::releaseSynch() // [[using locked(m_RcvBufferLock)]]; int32_t srt::CUDT::ackDataUpTo(int32_t ack) { - int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); + const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << ack << " vs. current %" << m_iRcvLastSkipAck << " (signing off " << acksize << " packets)"); @@ -7691,7 +7707,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; + int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; @@ -7881,7 +7897,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) data[ACKD_RCVLASTACK] = m_iRcvLastAck; data[ACKD_RTT] = m_iSRTT; data[ACKD_RTTVAR] = m_iRTTVar; - data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + data[ACKD_BUFFERLEFT] = getAvailRcvBufferSizeNoLock(); // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock if (data[ACKD_BUFFERLEFT] < 2) data[ACKD_BUFFERLEFT] = 2; @@ -9666,7 +9682,7 @@ int srt::CUDT::processData(CUnit* in_unit) continue; } - const int avail_bufsize = m_pRcvBuffer->getAvailBufSize(); + const int avail_bufsize = getAvailRcvBufferSizeNoLock(); if (offset >= avail_bufsize) { // This is already a sequence discrepancy. Probably there could be found @@ -9773,7 +9789,7 @@ int srt::CUDT::processData(CUnit* in_unit) LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset << " BUFr=" << avail_bufsize - << " avail=" << m_pRcvBuffer->getAvailBufSize() + << " avail=" << getAvailRcvBufferSizeNoLock() << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) @@ -11123,7 +11139,7 @@ void srt::CUDT::addEPoll(const int eid) return; enterCS(m_RecvLock); - if (m_pRcvBuffer->isRcvDataReady()) + if (isRcvBufferReady()) { uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } diff --git a/srtcore/core.h b/srtcore/core.h index e74c58c6e..2bbc7b069 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -692,6 +692,9 @@ class CUDT return m_stats.tsStartTime; } + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + bool isRcvBufferReady() const; + // TSBPD thread main function. static void* tsbpd(void* param); @@ -863,17 +866,17 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data - CRcvBuffer* m_pRcvBuffer; // Receiver buffer - CRcvLossList* m_pRcvLossList; // Receiver loss list - std::deque m_FreshLoss; // Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. - int m_iReorderTolerance; // Current value of dynamic reorder tolerance - int m_iConsecEarlyDelivery; // Increases with every OOO packet that came m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; //< Current value of dynamic reorder tolerance + int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_ACKWindow; // ACK history window CPktTimeWindow<16, 64> m_RcvTimeWindow; // Packet arrival time window - int32_t m_iRcvLastAck; // Last sent ACK + int32_t m_iRcvLastAck; // First unacknowledged packet seqno sent in the latest ACK. #ifdef ENABLE_LOGGING int32_t m_iDebugPrevLastAck; #endif @@ -921,7 +924,7 @@ class CUDT sync::Condition m_SendBlockCond; // used to block "send" call sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond - sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer + mutable sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer // Protects access to m_iSndCurrSeqNo, m_iSndLastAck sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) @@ -1033,6 +1036,15 @@ class CUDT int32_t ackDataUpTo(int32_t seq); void handleKeepalive(const char* data, size_t lenghth); + /// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer. + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + size_t getAvailRcvBufferSizeLock() const; + + /// Retrieves the available size of the receiver buffer. + /// Expects that m_RcvBufferLock is locked. + SRT_ATTR_REQUIRES(m_RcvBufferLock) + size_t getAvailRcvBufferSizeNoLock() const; + private: // Trace struct CoreStats { diff --git a/srtcore/sync.h b/srtcore/sync.h index 7cb0f63a9..b68061cf6 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -357,27 +357,25 @@ class SRT_ATTR_SCOPED_CAPABILITY ScopedLock class SRT_ATTR_SCOPED_CAPABILITY UniqueLock { friend class SyncEvent; + int m_iLocked; + Mutex& m_Mutex; public: - SRT_ATTR_ACQUIRE(m_Mutex) + SRT_ATTR_ACQUIRE(m) UniqueLock(Mutex &m); - SRT_ATTR_RELEASE(m_Mutex) + SRT_ATTR_RELEASE() ~UniqueLock(); public: - SRT_ATTR_ACQUIRE(m_Mutex) + SRT_ATTR_ACQUIRE() void lock(); - SRT_ATTR_RELEASE(m_Mutex) + SRT_ATTR_RELEASE() void unlock(); SRT_ATTR_RETURN_CAPABILITY(m_Mutex) Mutex* mutex(); // reflects C++11 unique_lock::mutex() - -private: - int m_iLocked; - Mutex& m_Mutex; }; #endif // ENABLE_STDCXX_SYNC From ac6ec62a9071b0f7c4410b211a9005792e5d3394 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 11 Oct 2021 17:06:27 +0200 Subject: [PATCH 285/790] [tests] Improved the TestConnection.Multiple test. Using std::array. Try to bind SRT directly instead of UDP. --- srtcore/channel.cpp | 2 +- test/test_many_connections.cpp | 80 +++++++++++++++------------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 92fd6b991..85c39ba95 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -584,7 +584,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > 8) { // Make a random number in the range between 8 and 24 - const int rnd = srt::sync::getRandomInt(8, 24); + const int rnd = srt::sync::genRandomInt(8, 24); if (dcounter > rnd) { diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index cca42ecf0..99bc5925c 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -1,8 +1,10 @@ #define _CRT_RAND_S // For Windows, rand_s #include +#include #include #include +#include #ifdef _WIN32 #include @@ -49,40 +51,36 @@ class TestConnection m_sa.sin_family = AF_INET; m_sa.sin_addr.s_addr = INADDR_ANY; - m_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - ASSERT_NE(m_udp_sock, -1); - // Find unused a port not used by any other service. - // Otherwise srt_connect may actually connect. - int bind_res = -1; + m_server_sock = srt_create_socket(); + ASSERT_NE(m_server_sock, SRT_INVALID_SOCK); + + // Find a port not used by another service. + int bind_res = 0; const sockaddr* psa = reinterpret_cast(&m_sa); - for (int port = 5000; port <= 5555; ++port) + for (int port = 5000; port <= 5100; ++port) { m_sa.sin_port = htons(port); - bind_res = ::bind(m_udp_sock, psa, sizeof m_sa); - if (bind_res >= 0) + + bind_res = srt_bind(m_server_sock, psa, sizeof m_sa); + if (bind_res == 0) { cerr << "Running test on port " << port << "\n"; break; } } - // Close the socket to free the port. - ASSERT_NE(closesocket(m_udp_sock), -1); - ASSERT_GE(bind_res, 0); + ASSERT_GE(bind_res, 0) << "srt_bind returned " << bind_res << ": " << srt_getlasterror_str(); ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1); // Fill the buffer with random data - time_t now; - time(&now); - unsigned int randseed = now % 255; - srand(randseed); - for (int i = 0; i < SRT_LIVE_DEF_PLSIZE; ++i) - buf[i] = rand_r(&randseed) % 255; + std::random_device rnd_device; + std::mt19937 gen(rnd_device()); + std::uniform_int_distribution dis(-128, 127); + std::generate(m_buf.begin(), m_buf.end(), [dis, gen]() mutable { return (char)dis(gen); }); - m_server_sock = srt_create_socket(); + cout << "Generated: " << static_cast(m_buf[0]) << ", " << static_cast(m_buf[1]) << std::endl; - ASSERT_NE(srt_bind(m_server_sock, psa, sizeof m_sa), -1); ASSERT_NE(srt_listen(m_server_sock, NSOCK), -1); } @@ -93,7 +91,6 @@ class TestConnection void AcceptLoop() { - //cerr << "[T] Accepting connections\n"; for (;;) { sockaddr_any addr; @@ -102,18 +99,16 @@ class TestConnection if (acp == -1) { cerr << "[T] Accept error at " << m_accepted.size() - << "/" << NSOCK << ": " << srt_getlasterror_str() << endl; + << "/" << NSOCK << ": " << srt_getlasterror_str() << endl; break; } - //cerr << "[T] Got new acp @" << acp << endl; m_accepted.push_back(acp); } - m_accept_exit = true; - cerr << "[T] Closing those accepted ones\n"; + m_accept_exit = true; - for (auto s: m_accepted) + for (const auto s : m_accepted) { srt_close(s); } @@ -122,12 +117,11 @@ class TestConnection } protected: - SOCKET m_udp_sock = INVALID_SOCKET; sockaddr_in m_sa = sockaddr_in(); SRTSOCKET m_server_sock = SRT_INVALID_SOCK; vector m_accepted; - char buf[SRT_LIVE_DEF_PLSIZE]; - SRTSOCKET srt_socket_list[NSOCK]; + std::array m_buf; + SRTSOCKET m_connections[NSOCK]; volatile bool m_accept_exit = false; }; @@ -135,55 +129,53 @@ class TestConnection TEST_F(TestConnection, Multiple) { - size_t size = SRT_LIVE_DEF_PLSIZE; - - sockaddr_in lsa = m_sa; - + const sockaddr_in lsa = m_sa; const sockaddr* psa = reinterpret_cast(&lsa); auto ex = std::async([this] { return AcceptLoop(); }); - cout << "Opening " << NSOCK << " connections\n"; + cerr << "Opening " << NSOCK << " connections\n"; for (size_t i = 0; i < NSOCK; i++) { - srt_socket_list[i] = srt_create_socket(); + m_connections[i] = srt_create_socket(); + EXPECT_NE(m_connections[i], SRT_INVALID_SOCK); // Give it 60s timeout, many platforms fail to process // so many connections in a short time. int conntimeo = 60; - srt_setsockflag(srt_socket_list[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); + srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n"; //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl; - ASSERT_NE(srt_connect(srt_socket_list[i], psa, sizeof lsa), SRT_ERROR); + ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR); // Set now async sending so that sending isn't blocked int no = 0; - ASSERT_NE(srt_setsockflag(srt_socket_list[i], SRTO_SNDSYN, &no, sizeof no), -1); + ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); } for (size_t j = 1; j <= 100; j++) { for (size_t i = 0; i < NSOCK; i++) { - EXPECT_GT(srt_send(srt_socket_list[i], buf, size), 0); + EXPECT_GT(srt_send(m_connections[i], m_buf.data(), (int) m_buf.size()), 0); } } - cout << "Sending finished, closing caller sockets\n"; + cerr << "Sending finished, closing caller sockets\n"; for (size_t i = 0; i < NSOCK; i++) { - EXPECT_EQ(srt_close(srt_socket_list[i]), SRT_SUCCESS); + EXPECT_EQ(srt_close(m_connections[i]), SRT_SUCCESS); } - // Up to this moment the server sock should survive - cout << "Closing server socket\n"; - - EXPECT_EQ(m_accept_exit, false); + 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); + cerr << "Synchronize with the accepting thread\n"; ex.wait(); } From 489c5fc6c6853531d62b9888e0b2bb019c70e358 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Oct 2021 16:01:29 +0200 Subject: [PATCH 286/790] [core] Use Mersenne Twister engine with C++11 instead of the std::random_device. Fixed max value probability for the default (C++03) rand(). --- srtcore/sync.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index aa15c2471..fdf5a0b66 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -283,10 +283,11 @@ bool srt::sync::CGlobEvent::waitForEvent() namespace srt { #if HAVE_CXX11 -static std::random_device& randomDevice() +static std::mt19937& randomGen() { static std::random_device s_RandomDevice; - return s_RandomDevice; + static std::mt19937 s_GenMT19937(s_RandomDevice()); + return s_GenMT19937; } #elif defined(_WIN32) && defined(__MINGW32__) static void initRandSeed() @@ -300,7 +301,7 @@ static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT; static unsigned int genRandSeed() { // Duration::count() does not depend on any global objects, - // therefore it is preferred over count)microseconds(..). + // therefore it is preferred over count_microseconds(..). const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); return (unsigned int) seed; } @@ -325,7 +326,7 @@ int srt::sync::genRandomInt(int minVal, int maxVal) sync::ScopedLock lck(s_mtxRandomDevice); #if HAVE_CXX11 uniform_int_distribution<> dis(minVal, maxVal); - return dis(randomDevice()); + return dis(randomGen()); #else #if defined(__MINGW32__) // No rand_r(..) for MinGW. @@ -342,9 +343,13 @@ int srt::sync::genRandomInt(int minVal, int maxVal) #endif // Map onto [minVal, maxVal]. - // Note. The probablity to get maxVal as the result is minuscule. - const int res = minVal + static_cast((maxVal - minVal) * rand_0_1); - return res; + // Note. There is a minuscule probablity to get maxVal+1 as the result. + // So we have to use long long to handle cases when maxVal = INT32_MAX. + // Also we must check 'res' does not exceed maxVal, + // which may happen if rand_0_1 = 1, even though the chances are low. + const long long llMaxVal = maxVal; + const int res = minVal + static_cast((llMaxVal + 1 - minVal) * rand_0_1); + return min(res, maxVal); #endif // HAVE_CXX11 } From fa1c3736cfd3a76f7892f186955aa57cbaaaa290 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Oct 2021 16:02:14 +0200 Subject: [PATCH 287/790] [tests] Added check for a minimal value of the uniform distribution. --- test/test_many_connections.cpp | 5 +++- test/test_sync.cpp | 50 ++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 99bc5925c..52d9be773 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -126,7 +126,10 @@ class TestConnection }; - +// This test establishes multiple connections to a single SRT listener on a localhost port. +// Packets are submitted for sending to all those connections in a non-blocking mode. +// Then all connections are closed. Some sockets may potentially still have undelivered packets. +// This test tries to reproduce the issue described in #1182, and fixed by #1315. TEST_F(TestConnection, Multiple) { const sockaddr_in lsa = m_sa; diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 6fa59c137..5e08cd30b 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -1,4 +1,5 @@ #include "gtest/gtest.h" +#include #include #include #include @@ -15,14 +16,6 @@ using namespace std; using namespace srt::sync; -// GNUC supports C++14 starting from version 5 -//#if defined(__GNUC__) && (__GNUC__ < 5) -////namespace srt -// constexpr chrono::milliseconds operator"" ms( -// unsigned long long _Val) { // return integral milliseconds -// return chrono::milliseconds(_Val); -//} -//#endif TEST(SyncDuration, BasicChecks) { @@ -159,23 +152,44 @@ TEST(SyncDuration, OperatorMultIntEq) TEST(SyncRandom, GenRandomInt) { - vector mn(64); + array mn = {}; - for (int i = 0; i < 2048; ++i) + // Check generated values are in the specified range. + const size_t n = 2048; + for (size_t i = 0; i < n; ++i) { - const int rand_val = genRandomInt(0, 63); + const int rand_val = genRandomInt(0, mn.size() - 1); ASSERT_GE(rand_val, 0); - ASSERT_LE(rand_val, 63); + ASSERT_LT(rand_val, mn.size()); ++mn[rand_val]; } + // Check the distribution is more or less uniform. + // 100% uniform if each value is generated (n / (2 * mn.size())) times. + // We expect at least half of that value for a random uniform distribution. + const int min_value = n / (2 * mn.size()) - 1; + cout << "min value: " << min_value << endl; + for (size_t i = 0; i < mn.size(); ++i) + { + EXPECT_GE(mn[i], min_value) << "i=" << i << ". Ok-ish if the count is non-zero."; + } + // Uncomment to see the distribution. - // for (size_t i = 0; i < mn.size(); ++i) - // { - // cout << i << '\t'; - // for (int j=0; j Date: Wed, 13 Oct 2021 02:57:21 -0500 Subject: [PATCH 288/790] [build] Fix build with default configuration for GCC <4.7 (#2160) --- CMakeLists.txt | 6 +++++- docs/build/build-options.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21478c36a..0bebae3cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,11 @@ endif() # options option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) -option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) +if (CMAKE_CXX_COMPILER_ID MATCHES "^GNU$" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) + option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" OFF) +else() + option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) +endif() option(ENABLE_APPS "Should the Support Applications be Built?" ON) option(ENABLE_EXPERIMENTAL_BONDING "Should the EXPERIMENTAL bonding functionality be enabled?" OFF) option(ENABLE_TESTING "Should the Developer Test Applications be Built?" OFF) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 938d925d2..d8c1d757a 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -74,7 +74,7 @@ The `pkg-confg` file (`srt.pc`) will be generated with the `libstdc++` library as a dependency. This may be required in some cases where you have an application written in C which therefore won't link against `libstdc++` by default. -**`--enable-c++11`** (default: ON) +**`--enable-c++11`** (default: ON except for GCC<4.7) Enable compiling in C++11 mode for those parts that may require it. Parts that don't require it will still be compiled in C++03 mode, From 2da63dc284aca2a90e4d9a21080cc8bcdd27968e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 16:08:56 +0200 Subject: [PATCH 289/790] [build] Add Android build action to GitHub CI (#2149) Also corrected the 'build-android.sh' script. Co-authored-by: Sergei Ignatov --- .github/workflows/android.yaml | 26 ++++++++++++++++++++++++++ scripts/build-android/build-android | 16 ++++------------ 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/android.yaml diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml new file mode 100644 index 000000000..3cdb0c79e --- /dev/null +++ b/.github/workflows/android.yaml @@ -0,0 +1,26 @@ +name: Android + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: NDK-R23 + runs-on: ubuntu-18.04 + + steps: + - name: Setup Android NDK R23 + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r23 + add-to-path: false + - uses: actions/checkout@v2 + - name: build + run: | + cd ./scripts/build-android/ + echo ${{ steps.setup-ndk.outputs.ndk-path }} + source ./build-android -n ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index f2d596847..b1c7363a9 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -10,7 +10,6 @@ echo_help() echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" echo " -o OpenSSL version. E.g. 1.1.1l" echo " -m Mbed TLS version. E.g. v2.26.0" - echo " -s SRT version. E.g. v1.4.4" echo echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\"" echo @@ -21,7 +20,6 @@ NDK_ROOT="" API_LEVEL=28 BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" OPENSSL_VERSION=1.1.1l -SRT_VERSION="" ENC_LIB=openssl MBEDTLS_VERSION=v2.26.0 @@ -56,10 +54,8 @@ SCRIPT_DIR=$(pwd) HOST_TAG='unknown' unamestr=$(uname -s) if [ "$unamestr" = 'Linux' ]; then - SCRIPT_DIR=$(readlink -f $0 | xargs dirname) HOST_TAG='linux-x86_64' elif [ "$unamestr" = 'Darwin' ]; then - SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) if [ $(uname -p) = 'arm' ]; then echo "NDK does not currently support ARM64" exit 128 @@ -93,12 +89,8 @@ else exit 128 fi -if [ ! -d $BASE_DIR/srt ]; then - git clone https://github.com/Haivision/srt srt - if [ ! -z "$SRT_VERSION" ]; then - git -C $BASE_DIR/srt checkout $SRT_VERSION - fi -fi +# Build working copy of srt repository +REPO_DIR="../.." for build_target in $BUILD_TARGETS; do LIB_DIR=$BASE_DIR/$build_target/lib @@ -113,7 +105,7 @@ for build_target in $BUILD_TARGETS; do cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so fi - git -C $BASE_DIR/srt clean -fd - $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + git -C $REPO_DIR clean -fd -e scripts + $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $REPO_DIR -i $BASE_DIR/$build_target cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done From 8bcf30bfd8f1a85d2147ae930f9eed5c1467076d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 12:43:25 +0200 Subject: [PATCH 290/790] [tests] More RCV buffer unit tests --- test/test_buffer.cpp | 253 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 242 insertions(+), 11 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index d2e7b39f6..a08aa2406 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -1,20 +1,23 @@ #include #include "gtest/gtest.h" #include "buffer.h" +#include using namespace srt; +using namespace std; - +// Check the available size of the receiver buffer. TEST(CRcvBuffer, Create) { const int buffer_size_pkts = 128; CUnitQueue unit_queue; CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic + // One buffer cell is always unavailable as it is use to distinguish + // two buffer states: empty and full. + EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); } - +// Fill the buffer full, and check adding more data results in an error. TEST(CRcvBuffer, FullBuffer) { const int buffer_size_pkts = 16; @@ -51,10 +54,10 @@ TEST(CRcvBuffer, FullBuffer) } } - +// BUG!!! // In this test case a packet is added to receiver buffer with offset 1, // thus leaving offset 0 with an empty pointer. -// The buffer sais it is not empty, and the data is available +// The buffer says it is not empty, and the data is available // to be read, but reading should cause error. TEST(CRcvBuffer, ReadDataIPE) { @@ -73,18 +76,19 @@ TEST(CRcvBuffer, ReadDataIPE) EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); + + // BUG. Acknowledging an empty position must not result in a read-readiness. rcv_buffer.ackData(1); EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - - std::cerr << "Expecting IPE message: \n"; - std::array buff; + cerr << "Expecting IPE message: \n"; + array buff; const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); EXPECT_EQ(res, -1); } - TEST(CRcvBuffer, ReadData) { const int buffer_size_pkts = 16; @@ -112,7 +116,6 @@ TEST(CRcvBuffer, ReadData) EXPECT_EQ(size_t(res), payload_size); } - TEST(CRcvBuffer, AddData) { const int buffer_size_pkts = 16; @@ -159,4 +162,232 @@ TEST(CRcvBuffer, AddData) EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); } +TEST(CRcvBuffer, OneMessageInSeveralPackets) +{ + const int buffer_size_pkts = 16; + CUnitQueue unit_queue; + unit_queue.init(buffer_size_pkts, 1500, AF_INET); + CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); + + const int initial_seqno = 1000; + const int message_len_in_pkts = 4; + const size_t payload_size = 1456; + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < message_len_in_pkts; ++i) + { + CUnit* unit = unit_queue.getNextAvailUnit(); + EXPECT_NE(unit, nullptr); + + CPacket& packet = unit->m_Packet; + packet.setLength(payload_size); + packet.m_iSeqNo = initial_seqno + i; + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + if (i == 0) + packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + const bool is_last_packet = (i == message_len_in_pkts - 1); + if (is_last_packet) + packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + + EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + } + + rcv_buffer.ackData(message_len_in_pkts); + + cout << "Buffer size before reading: " << rcv_buffer.getAvailBufSize() << endl; + std::array buff; + cout << "Reading one packet of the 4-packet message" << endl; + const int res = rcv_buffer.readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, payload_size); + cout << "Buffer size after reading: " << rcv_buffer.getAvailBufSize() << endl; +} + +class TestRcvBufferRead + : public ::testing::Test +{ +protected: + TestRcvBufferRead() + { + // initialization code here + } + + ~TestRcvBufferRead() + { + // cleanup any pending stuff, but no exceptions allowed + } +protected: + // SetUp() is run immediately before a test starts. + void SetUp() override + { + // make_unique is unfortunatelly C++14 + m_unit_queue = unique_ptr(new CUnitQueue); + m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + } + + void TearDown() override + { + // Code here will be called just after the test completes. + // OK to throw exceptions from here if needed. + m_unit_queue.reset(); + m_rcv_buffer.reset(); + } + +public: + void addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false) + { + const int msg_offset = start_seqno - m_init_seqno; + + for (size_t i = 0; i < msg_len_pkts; ++i) + { + CUnit* unit = m_unit_queue->getNextAvailUnit(); + EXPECT_NE(unit, nullptr); + + CPacket& packet = unit->m_Packet; + packet.setLength(m_payload_sz); + packet.m_iSeqNo = start_seqno + i; + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + if (i == 0) + packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + const bool is_last_packet = (i == (msg_len_pkts - 1)); + if (is_last_packet) + packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); + EXPECT_TRUE(packet.getMsgOrderFlag()); + } + + EXPECT_EQ(m_rcv_buffer->addData(unit, msg_offset + i), 0); + } + } + +protected: + unique_ptr m_unit_queue; + unique_ptr m_rcv_buffer; + const int m_buff_size_pkts = 16; + const int m_init_seqno = 1000; + static const size_t m_payload_sz = 1456; +}; + +TEST_F(TestRcvBufferRead, OnePacket) +{ + const size_t msg_pkts = 1; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // Full ACK + m_rcv_buffer->ackData(msg_pkts); + + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +TEST_F(TestRcvBufferRead, OnePacketWithGap) +{ + const size_t msg_pkts = 1; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno + 1, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // ACK first missing packet + m_rcv_buffer->ackData(msg_pkts); + + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + m_rcv_buffer->ackData(msg_pkts); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// Check reading the whole message (consisting of several packets) from the buffer. +TEST_F(TestRcvBufferRead, MsgAcked) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Acknowledge all packets of the message. + m_rcv_buffer->ackData(4); + // Now the whole message can be read. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// BUG!!! +// Checks signalling of read-readiness of a half-acknowledged message. +// The RCV buffer implementation has an issue here: when only half of the message is +// acknowledged, the RCV buffer signals read-readiness, even though +// the message can't be read, and reading returns 0. +TEST_F(TestRcvBufferRead, MsgHalfAck) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + // Nothing to read (0 for zero bytes read). + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // ACK half of the message and check read-readiness. + m_rcv_buffer->ackData(2); + // FIXME: Sadly RCV buffer says the data is ready to be read. + // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + // EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + // Actually must be nothing to read (can't read half a message). + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); +} + +// BUG!!! +// Adding a message with the out-of-order flag set. +// RCV buffer does not signal read-readiness, but actually the packet can be read. +TEST_F(TestRcvBufferRead, OutOfOrderMsgNoACK) +{ + const size_t msg_pkts = 4; + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + addMessage(msg_pkts, m_init_seqno, true); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} From 6e896399e3b4fdf09bd11179ecd02145727fd5ee Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 16:07:42 +0200 Subject: [PATCH 291/790] [tests] Fixed async launch TestConnection.Multiple --- test/test_many_connections.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 52d9be773..759b6a461 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -79,8 +79,6 @@ class TestConnection std::uniform_int_distribution dis(-128, 127); std::generate(m_buf.begin(), m_buf.end(), [dis, gen]() mutable { return (char)dis(gen); }); - cout << "Generated: " << static_cast(m_buf[0]) << ", " << static_cast(m_buf[1]) << std::endl; - ASSERT_NE(srt_listen(m_server_sock, NSOCK), -1); } @@ -135,7 +133,7 @@ TEST_F(TestConnection, Multiple) const sockaddr_in lsa = m_sa; const sockaddr* psa = reinterpret_cast(&lsa); - auto ex = std::async([this] { return AcceptLoop(); }); + auto ex = std::async(std::launch::async, [this] { return AcceptLoop(); }); cerr << "Opening " << NSOCK << " connections\n"; @@ -180,6 +178,7 @@ TEST_F(TestConnection, Multiple) cerr << "Synchronize with the accepting thread\n"; ex.wait(); + cerr << "Synchronization done\n"; } From a5b61db5b8e2e6f906665e31d7ec8179175846ed Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 18 Oct 2021 09:30:06 +0200 Subject: [PATCH 292/790] [build] Fixed missing ws2_32 lib in Windows installer (#2169) The property file libsrt.props did not reference ws2_32.lib. For large applications which already reference the Windows socket library, there is no difference. In small test applications which reference libsrt only, there were undefined symbols. --- scripts/win-installer/libsrt.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props index 01e0b5dac..b1da74879 100644 --- a/scripts/win-installer/libsrt.props +++ b/scripts/win-installer/libsrt.props @@ -29,7 +29,7 @@ $(LIBSRT)\include;%(AdditionalIncludeDirectories) - srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) + srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;ws2_32.lib;%(AdditionalDependencies) $(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories) /ignore:4099 %(AdditionalOptions) From c1fdb61292358c604cb956fef3d6e768d7e43be6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 18 Oct 2021 15:47:14 +0200 Subject: [PATCH 293/790] [core] Changed lock order in bstats (#2168) --- srtcore/core.cpp | 242 ++++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 120 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b3b1d374c..787533cea 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7130,98 +7130,129 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - ScopedLock statsguard(m_StatsLock); - - const steady_clock::time_point currtime = steady_clock::now(); - - perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); - perf->pktSent = m_stats.traceSent; - perf->pktSentUnique = m_stats.traceSentUniq; - perf->pktRecv = m_stats.traceRecv; - perf->pktRecvUnique = m_stats.traceRecvUniq; - perf->pktSndLoss = m_stats.traceSndLoss; - perf->pktRcvLoss = m_stats.traceRcvLoss; - perf->pktRetrans = m_stats.traceRetrans; - perf->pktRcvRetrans = m_stats.traceRcvRetrans; - perf->pktSentACK = m_stats.sentACK; - perf->pktRecvACK = m_stats.recvACK; - perf->pktSentNAK = m_stats.sentNAK; - perf->pktRecvNAK = m_stats.recvNAK; - perf->usSndDuration = m_stats.sndDuration; - perf->pktReorderDistance = m_stats.traceReorderDistance; - perf->pktReorderTolerance = m_iReorderTolerance; - perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; - perf->pktRcvBelated = m_stats.traceRcvBelated; - - perf->pktSndFilterExtra = m_stats.sndFilterExtra; - perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; - perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; - perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; - - /* perf byte counters include all headers (SRT+UDP+IP) */ const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); - perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); - perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); - perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); - perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); - perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); - - perf->pktSndDrop = m_stats.traceSndDrop; - perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; - perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); - perf->byteRcvDrop = - m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; - perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; - perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; - - perf->pktSentTotal = m_stats.sentTotal; - perf->pktSentUniqueTotal = m_stats.sentUniqTotal; - perf->pktRecvTotal = m_stats.recvTotal; - perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; - perf->pktSndLossTotal = m_stats.sndLossTotal; - perf->pktRcvLossTotal = m_stats.rcvLossTotal; - perf->pktRetransTotal = m_stats.retransTotal; - perf->pktSentACKTotal = m_stats.sentACKTotal; - perf->pktRecvACKTotal = m_stats.recvACKTotal; - perf->pktSentNAKTotal = m_stats.sentNAKTotal; - perf->pktRecvNAKTotal = m_stats.recvNAKTotal; - perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - - perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); - perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); - perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); - perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); - perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); - perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; - perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; - perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; - perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; - - perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); - perf->pktSndDropTotal = m_stats.sndDropTotal; - perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; - perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); - perf->byteRcvDropTotal = - m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; - perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; - perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; - - const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); - perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; - 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->pktFlightSize = getFlightSpan(); - perf->msRTT = (double)m_iSRTT / 1000.0; - perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; - perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; - perf->byteMSS = m_config.iMSS; - - perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW) - : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) - : 0; + { + ScopedLock statsguard(m_StatsLock); + + const steady_clock::time_point currtime = steady_clock::now(); + + perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); + perf->pktSent = m_stats.traceSent; + perf->pktSentUnique = m_stats.traceSentUniq; + perf->pktRecv = m_stats.traceRecv; + perf->pktRecvUnique = m_stats.traceRecvUniq; + perf->pktSndLoss = m_stats.traceSndLoss; + perf->pktRcvLoss = m_stats.traceRcvLoss; + perf->pktRetrans = m_stats.traceRetrans; + perf->pktRcvRetrans = m_stats.traceRcvRetrans; + perf->pktSentACK = m_stats.sentACK; + perf->pktRecvACK = m_stats.recvACK; + perf->pktSentNAK = m_stats.sentNAK; + perf->pktRecvNAK = m_stats.recvNAK; + perf->usSndDuration = m_stats.sndDuration; + perf->pktReorderDistance = m_stats.traceReorderDistance; + perf->pktReorderTolerance = m_iReorderTolerance; + perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; + perf->pktRcvBelated = m_stats.traceRcvBelated; + + perf->pktSndFilterExtra = m_stats.sndFilterExtra; + perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; + perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; + perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; + + /* perf byte counters include all headers (SRT+UDP+IP) */ + perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); + perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); + perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); + perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); + perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); + perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); + + perf->pktSndDrop = m_stats.traceSndDrop; + perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; + perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); + perf->byteRcvDrop = + m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; + perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; + perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; + + perf->pktSentTotal = m_stats.sentTotal; + perf->pktSentUniqueTotal = m_stats.sentUniqTotal; + perf->pktRecvTotal = m_stats.recvTotal; + perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; + perf->pktSndLossTotal = m_stats.sndLossTotal; + perf->pktRcvLossTotal = m_stats.rcvLossTotal; + perf->pktRetransTotal = m_stats.retransTotal; + perf->pktSentACKTotal = m_stats.sentACKTotal; + perf->pktRecvACKTotal = m_stats.recvACKTotal; + perf->pktSentNAKTotal = m_stats.sentNAKTotal; + perf->pktRecvNAKTotal = m_stats.recvNAKTotal; + perf->usSndDurationTotal = m_stats.m_sndDurationTotal; + + perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); + perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); + perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); + perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); + perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; + perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; + perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; + perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; + + perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); + perf->pktSndDropTotal = m_stats.sndDropTotal; + perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; + perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); + perf->byteRcvDropTotal = + m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; + perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; + perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; + + // TODO: The following class members must be protected with a different mutex, not the m_StatsLock. + const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); + perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; + 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->pktFlightSize = getFlightSpan(); + perf->msRTT = (double)m_iSRTT / 1000.0; + perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; + perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; + perf->byteMSS = m_config.iMSS; + + perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW) + : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) + : 0; + + if (clear) + { + m_stats.traceSndDrop = 0; + m_stats.traceRcvDrop = 0; + m_stats.traceSndBytesDrop = 0; + m_stats.traceRcvBytesDrop = 0; + m_stats.traceRcvUndecrypt = 0; + m_stats.traceRcvBytesUndecrypt = 0; + m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; + m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; + m_stats.traceSent = m_stats.traceRecv + = m_stats.traceSentUniq = m_stats.traceRecvUniq + = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans + = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; + m_stats.sndDuration = 0; + m_stats.traceRcvRetrans = 0; + m_stats.traceRcvBelated = 0; + m_stats.traceRcvBytesLoss = 0; + + m_stats.sndFilterExtra = 0; + m_stats.rcvFilterExtra = 0; + + m_stats.rcvFilterSupply = 0; + m_stats.rcvFilterLoss = 0; + + m_stats.tsLastSampleTime = currtime; + } + } const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth.load(); @@ -7242,7 +7273,6 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktSndBuf = m_pSndBuffer->getAvgBufSize((perf->byteSndBuf), (perf->msSndBuf)); } perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize); - //< perf->byteAvailSndBuf = (m_config.iSndBufSize - perf->pktSndBuf) * m_config.iMSS; } else @@ -7285,34 +7315,6 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->byteRcvBuf = 0; perf->msRcvBuf = 0; } - - if (clear) - { - m_stats.traceSndDrop = 0; - m_stats.traceRcvDrop = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.traceRcvUndecrypt = 0; - m_stats.traceRcvBytesUndecrypt = 0; - m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; - m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; - m_stats.traceSent = m_stats.traceRecv - = m_stats.traceSentUniq = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.sndDuration = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceRcvBelated = 0; - m_stats.traceRcvBytesLoss = 0; - - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; - - m_stats.tsLastSampleTime = currtime; - } } bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) From 3f2945caff59c50e1e66c5719e473aea5f8ad4e0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 18 Oct 2021 18:45:28 +0200 Subject: [PATCH 294/790] [core] Reduced nesting of checkBrokenSockets() --- srtcore/api.cpp | 106 ++++++++++++++++++++++-------------------------- srtcore/core.h | 3 +- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e35d637a5..59399a4b3 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2572,10 +2572,6 @@ void srt::CUDTUnited::checkBrokenSockets() { ScopedLock cg(m_GlobControlLock); - // set of sockets To Be Closed and To Be Removed - vector tbc; - vector tbr; - #if ENABLE_EXPERIMENTAL_BONDING vector delgids; @@ -2600,74 +2596,70 @@ void srt::CUDTUnited::checkBrokenSockets() { m_ClosedGroups.erase(*di); } - #endif - for (sockets_t::iterator i = m_Sockets.begin(); - i != m_Sockets.end(); ++ i) + // set of sockets To Be Closed and To Be Removed + vector tbc; + vector tbr; + + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i) { - CUDTSocket* s = i->second; + CUDTSocket* s = i->second; + if (!s->core().m_bBroken) + continue; - // check broken connection - if (s->core().m_bBroken) + if (s->m_Status == SRTS_LISTENING) { - if (s->m_Status == SRTS_LISTENING) - { - const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; - // for a listening socket, it should wait an extra 3 seconds - // in case a client is connecting - if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) + const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; + // A listening socket should wait an extra 3 seconds + // in case a client is connecting. + 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->isRcvDataAvailable()) + { + const int bc = s->core().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; } - } - 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->isRcvDataAvailable()) - { - const int bc = s->core().m_iBrokenCounter.load(); - if (bc > 0) - { - // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): - // %d\n", i->first); - // if there is still data in the receiver buffer, wait longer - s->core().m_iBrokenCounter.store(bc - 1); - continue; - } - } + } #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_GroupOf) - { - LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); - s->removeFromGroup(true); - } + if (s->m_GroupOf) + { + LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); + s->removeFromGroup(true); + } #endif - HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); + HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); - //close broken connections and start removal timer - s->setClosed(); - tbc.push_back(i->first); - m_ClosedSockets[i->first] = s; + //close broken connections and start removal timer + s->setClosed(); + tbc.push_back(i->first); + m_ClosedSockets[i->first] = s; - // remove from listener's queue - sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); - if (ls == m_Sockets.end()) - { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } - - enterCS(ls->second->m_AcceptLock); - ls->second->m_QueuedSockets.erase(s->m_SocketID); - leaveCS(ls->second->m_AcceptLock); + // remove from listener's queue + sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(s->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; } + + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); } for (sockets_t::iterator j = m_ClosedSockets.begin(); diff --git a/srtcore/core.h b/srtcore/core.h index 2bbc7b069..523118fce 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -749,7 +749,8 @@ class CUDT sync::atomic m_bPeerHealth; // If the peer status is normal sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + // A counter (number of GC checks happening every 1s) to let the GC tag this socket as closed. + sync::atomic m_iBrokenCounter; // If a broken socket still has data in the receiver buffer, it is not marked closed until the counter is 0. int m_iEXPCount; // Expiration counter sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second From 8f2d3182e6c99bdbe3f3af7427b0d610086ea074 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 20 Oct 2021 15:50:17 +0200 Subject: [PATCH 295/790] [tests] Refactored RCV buffer unit tests (#2170) --- srtcore/buffer.cpp | 2 +- test/test_buffer.cpp | 554 +++++++++++++++++++++++-------------------- 2 files changed, 294 insertions(+), 262 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 329b054a0..794066702 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -932,7 +932,7 @@ int CRcvBuffer::readBuffer(char* data, int len) { if (m_pUnit[p] == NULL) { - LOGC(brlog.Error, log << CONID() << " IPE readBuffer on null packet pointer"); + LOGC(brlog.Error, log << CONID() << "IPE readBuffer on null packet pointer"); return -1; } diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index a08aa2406..851fe1167 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -1,267 +1,125 @@ #include +#include #include "gtest/gtest.h" #include "buffer.h" -#include using namespace srt; using namespace std; -// Check the available size of the receiver buffer. -TEST(CRcvBuffer, Create) -{ - const int buffer_size_pkts = 128; - CUnitQueue unit_queue; - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - // One buffer cell is always unavailable as it is use to distinguish - // two buffer states: empty and full. - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); -} - -// Fill the buffer full, and check adding more data results in an error. -TEST(CRcvBuffer, FullBuffer) +class CRcvBufferReadMsg + : public ::testing::Test { - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - for (int i = 0; i < rcv_buffer.getAvailBufSize(); ++i) +protected: + CRcvBufferReadMsg() { - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + // initialization code here } - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic - - rcv_buffer.ackData(buffer_size_pkts - 1); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), 0); - - // Try to add more data than the available size of the buffer - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); - - std::array buff; - for (int i = 0; i < buffer_size_pkts - 1; ++i) + ~CRcvBufferReadMsg() { - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); + // cleanup any pending stuff, but no exceptions allowed } -} - -// BUG!!! -// In this test case a packet is added to receiver buffer with offset 1, -// thus leaving offset 0 with an empty pointer. -// The buffer says it is not empty, and the data is available -// to be read, but reading should cause error. -TEST(CRcvBuffer, ReadDataIPE) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 1), 0); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - - // BUG. Acknowledging an empty position must not result in a read-readiness. - rcv_buffer.ackData(1); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - cerr << "Expecting IPE message: \n"; - array buff; - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, -1); -} - -TEST(CRcvBuffer, ReadData) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 0), 0); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - rcv_buffer.ackData(1); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - - std::array buff; - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); -} - -TEST(CRcvBuffer, AddData) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic - - const size_t payload_size = 1456; - // Add 10 units (packets) to the buffer - for (int i = 0; i < 10; ++i) +protected: + // SetUp() is run immediately before a test starts. + void SetUp() override { - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + // make_unique is unfortunatelly C++14 + m_unit_queue = unique_ptr(new CUnitQueue); + ASSERT_NE(m_unit_queue.get(), nullptr); + m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + ASSERT_NE(m_rcv_buffer.get(), nullptr); } - // The available buffer size remains the same - // The value is reported by SRT receiver like this: - // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - - // Now acknowledge two packets - const int ack_pkts = 2; - rcv_buffer.ackData(2); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1 - ack_pkts); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - - std::array buff; - for (int i = 0; i < ack_pkts; ++i) + void TearDown() override { - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - ack_pkts + i); + // Code here will be called just after the test completes. + // OK to throw exceptions from here if needed. + m_unit_queue.reset(); + m_rcv_buffer.reset(); } - // Add packet to the same position - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); -} - -TEST(CRcvBuffer, OneMessageInSeveralPackets) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const int initial_seqno = 1000; - const int message_len_in_pkts = 4; - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - for (int i = 0; i < message_len_in_pkts; ++i) +public: + /// Generate and add one packet to the receiver buffer. + /// + /// @returns the result of rcv_buffer::insert(..) + int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) { - CUnit* unit = unit_queue.getNextAvailUnit(); + CUnit* unit = m_unit_queue->getNextAvailUnit(); EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.setLength(payload_size); - packet.m_iSeqNo = initial_seqno + i; + packet.m_iSeqNo = seqno; + packet.m_iTimeStamp = ts; + + packet.setLength(m_payload_sz); + generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); - if (i == 0) + if (pb_first) packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); - const bool is_last_packet = (i == message_len_in_pkts - 1); - if (is_last_packet) + if (pb_last) packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + if (!out_of_order) + { + packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); + EXPECT_TRUE(packet.getMsgOrderFlag()); + } + + const int offset = CSeqNo::seqoff(m_first_unack_seqno, seqno); + return m_rcv_buffer->addData(unit, offset); } - rcv_buffer.ackData(message_len_in_pkts); + /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed + int addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false, int ts = 0) + { + for (size_t i = 0; i < msg_len_pkts; ++i) + { + const bool pb_first = (i == 0); + const bool pb_last = (i == (msg_len_pkts - 1)); + const int res = addPacket(start_seqno + i, pb_first, pb_last, out_of_order, ts); - cout << "Buffer size before reading: " << rcv_buffer.getAvailBufSize() << endl; - std::array buff; - cout << "Reading one packet of the 4-packet message" << endl; - const int res = rcv_buffer.readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); - cout << "Buffer size after reading: " << rcv_buffer.getAvailBufSize() << endl; -} + if (res != 0) + return res; + } + return 0; + } -class TestRcvBufferRead - : public ::testing::Test -{ -protected: - TestRcvBufferRead() + void generatePayload(char* dst, size_t len, int seqno) { std::iota(dst, dst + len, (char)seqno); } + + bool verifyPayload(char* dst, size_t len, int seqno) { - // initialization code here + // Note. A more consistent way would be to use generatePayload function, + // but I don't want to create another buffer for the data. + for (size_t i = 0; i < len; ++i) + { + if (dst[i] != static_cast(seqno + i)) + return false; + } + return true; } - ~TestRcvBufferRead() + int ackPackets(int num_pkts) { - // cleanup any pending stuff, but no exceptions allowed + m_first_unack_seqno = CSeqNo::incseq(m_first_unack_seqno, num_pkts); + return m_rcv_buffer->ackData(num_pkts); } -protected: - // SetUp() is run immediately before a test starts. - void SetUp() override + int getAvailBufferSize() { - // make_unique is unfortunatelly C++14 - m_unit_queue = unique_ptr(new CUnitQueue); - m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); - m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + return m_rcv_buffer->getAvailBufSize(); } - void TearDown() override + int readMessage(char* data, size_t len) { - // Code here will be called just after the test completes. - // OK to throw exceptions from here if needed. - m_unit_queue.reset(); - m_rcv_buffer.reset(); + return m_rcv_buffer->readMsg(data, len); } -public: - void addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false) + bool hasAvailablePackets() { - const int msg_offset = start_seqno - m_init_seqno; - - for (size_t i = 0; i < msg_len_pkts; ++i) - { - CUnit* unit = m_unit_queue->getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - - CPacket& packet = unit->m_Packet; - packet.setLength(m_payload_sz); - packet.m_iSeqNo = start_seqno + i; - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); - if (i == 0) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); - const bool is_last_packet = (i == (msg_len_pkts - 1)); - if (is_last_packet) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); - - if (!out_of_order) - { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); - EXPECT_TRUE(packet.getMsgOrderFlag()); - } - - EXPECT_EQ(m_rcv_buffer->addData(unit, msg_offset + i), 0); - } + return m_rcv_buffer->isRcvDataAvailable(); } protected: @@ -269,63 +127,149 @@ class TestRcvBufferRead unique_ptr m_rcv_buffer; const int m_buff_size_pkts = 16; const int m_init_seqno = 1000; + int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; }; -TEST_F(TestRcvBufferRead, OnePacket) +// Check the available size of the receiver buffer. +TEST_F(CRcvBufferReadMsg, Create) { - const size_t msg_pkts = 1; - // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno, false); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); +} - const size_t msg_bytelen = msg_pkts * m_payload_sz; - array buff; +// Fill the buffer full, and check adding more data results in an error. +TEST_F(CRcvBufferReadMsg, FullBuffer) +{ + auto& rcv_buffer = *m_rcv_buffer.get(); + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < getAvailBufferSize(); ++i) + { + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + } + + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); // logic - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + ackPackets(m_buff_size_pkts - 1); + EXPECT_EQ(getAvailBufferSize(), 0); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + // Try to add more data than the available size of the buffer + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, getAvailBufferSize())), -1); + + array buff; + for (int i = 0; i < m_buff_size_pkts - 1; ++i) + { + const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } +} + +// BUG in the new RCV buffer!!! +// In this test case a packet is added to receiver buffer with offset 1, +// thus leaving offset 0 with an empty pointer. +// The buffer says it is not empty, and the data is available +// to be read, but reading is not possible. +TEST_F(CRcvBufferReadMsg, OnePacketGap) +{ + // Add one packet message to to the buffer + // with a gap of one packet. + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0); + + auto& rcv_buffer = *m_rcv_buffer.get(); + // Before ACK the available buffer size stays the same. + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + // Not available for reading as not yet acknowledged. + EXPECT_FALSE(hasAvailablePackets()); + // Confirm reading zero bytes. + array buff; + int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); - // Full ACK - m_rcv_buffer->ackData(msg_pkts); + // BUG. Acknowledging an empty position must not result in a read-readiness. + ackPackets(1); + // Wrong behavior (BUG) + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 2); + cerr << "Expecting IPE from readBuffer(..): \n"; + res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_EQ(res, -1); - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, 0); } -TEST_F(TestRcvBufferRead, OnePacketWithGap) +// Add one packet to the buffer and read it once it is acknowledged. +// Confirm the data read is valid. +TEST_F(CRcvBufferReadMsg, OnePacket) { const size_t msg_pkts = 1; // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno + 1, false); + addMessage(msg_pkts, m_init_seqno, false); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_FALSE(hasAvailablePackets()); + const int res1 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res1, 0); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + // Full ACK + ackPackets(msg_pkts); + EXPECT_TRUE(hasAvailablePackets()); - // ACK first missing packet - m_rcv_buffer->ackData(msg_pkts); + const int res2 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res2, msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); +} - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); +// Add ten packets to the buffer, acknowledge and read some of them. +// Then try to add packets to the position of existing packets. +// We can't check adding to the position of those packets already read, +// because a negative offset is not checked by the receiver buffer, +// but must be handled by the CUDT socket. +TEST_F(CRcvBufferReadMsg, AddData) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + } - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_FALSE(hasAvailablePackets()); - m_rcv_buffer->ackData(msg_pkts); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + // Now acknowledge two packets + const int ack_pkts = 2; + ackPackets(2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts); + EXPECT_TRUE(hasAvailablePackets()); - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + std::array buff; + for (int i = 0; i < ack_pkts; ++i) + { + const int res = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } + + // Add packet to the position of oackets already read. + // Can't check, as negative offset is an error not handled by the receiver buffer. + //EXPECT_EQ(addPacket(m_init_seqno), -1); + + // Add packet to a non-empty position. + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); } // Check reading the whole message (consisting of several packets) from the buffer. -TEST_F(TestRcvBufferRead, MsgAcked) +TEST_F(CRcvBufferReadMsg, MsgAcked) { const size_t msg_pkts = 4; // Adding one message without acknowledging @@ -335,12 +279,47 @@ TEST_F(TestRcvBufferRead, MsgAcked) array buff; // Acknowledge all packets of the message. - m_rcv_buffer->ackData(4); + ackPackets(msg_pkts); // Now the whole message can be read. EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); - const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +} + +// Check reading the whole message (consisting of several packets) into +// a buffer of an insufficient size. +TEST_F(CRcvBufferReadMsg, SmallReadBuffer) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Acknowledge all packets of the message. + ackPackets(msg_pkts); + // Now the whole message can be read. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + // Check reading into an insufficient size buffer. + // The old buffer extracts the whole message, but copies only + // the number of bytes provided in the 'len' argument. + const int res = readMessage(buff.data(), m_payload_sz); + EXPECT_EQ(res, 1456); + + // No more messages to read + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); } // BUG!!! @@ -348,7 +327,7 @@ TEST_F(TestRcvBufferRead, MsgAcked) // The RCV buffer implementation has an issue here: when only half of the message is // acknowledged, the RCV buffer signals read-readiness, even though // the message can't be read, and reading returns 0. -TEST_F(TestRcvBufferRead, MsgHalfAck) +TEST_F(CRcvBufferReadMsg, MsgHalfAck) { const size_t msg_pkts = 4; // Adding one message without acknowledging @@ -358,36 +337,89 @@ TEST_F(TestRcvBufferRead, MsgHalfAck) const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_FALSE(hasAvailablePackets()); + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); // ACK half of the message and check read-readiness. - m_rcv_buffer->ackData(2); + ackPackets(2); // FIXME: Sadly RCV buffer says the data is ready to be read. // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - // EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + // EXPECT_FALSE(hasAvailablePackets()); EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_TRUE(hasAvailablePackets()); // Actually must be nothing to read (can't read half a message). - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + const int res2 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res2, 0); } // BUG!!! // Adding a message with the out-of-order flag set. // RCV buffer does not signal read-readiness, but actually the packet can be read. -TEST_F(TestRcvBufferRead, OutOfOrderMsgNoACK) +TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) { const size_t msg_pkts = 4; // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, m_init_seqno, true); EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_FALSE(hasAvailablePackets()); + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// Adding a message with the out-of-order flag set. +// The message can be read. +TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) +{ + const size_t msg_pkts = 4; + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + addMessage(msg_pkts, CSeqNo::incseq(m_init_seqno, 1), true); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; - const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, 1 + i))); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + //int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) + const int res2 = addPacket(CSeqNo::incseq(m_init_seqno, 1)); + EXPECT_EQ(res2, -1); // already exists + + EXPECT_EQ(addPacket(m_init_seqno), 0); + ackPackets(msg_pkts + 1); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res3 = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(res3 == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, m_init_seqno)); + + // Only "passack" packets remain in the buffer. + // They are falsely signalled as read-ready. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); // BUG: nothing to read. + EXPECT_TRUE(hasAvailablePackets()); // BUG: nothing to read. + + // Adding a packet right after the EntryState_Read packets. + const int seqno = CSeqNo::incseq(m_init_seqno, msg_pkts + 1); + EXPECT_EQ(addPacket(seqno), 0); + ackPackets(1); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); } From 276a841da020eadf7da304cf03e1d62c63341748 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 22 Oct 2021 14:04:06 +0200 Subject: [PATCH 296/790] [core] New receiver buffer implementation --- CMakeLists.txt | 11 +- docs/build/build-options.md | 5 + srtcore/api.cpp | 6 + srtcore/buffer.cpp | 4 + srtcore/buffer.h | 4 + srtcore/buffer_rcv.cpp | 969 ++++++++++++++++++++++++++++++++++++ srtcore/buffer_rcv.h | 343 +++++++++++++ srtcore/core.cpp | 282 ++++++++++- srtcore/core.h | 9 + srtcore/filelist.maf | 2 + srtcore/queue.h | 1 + srtcore/utilities.h | 64 +++ 12 files changed, 1683 insertions(+), 17 deletions(-) create mode 100644 srtcore/buffer_rcv.cpp create mode 100644 srtcore/buffer_rcv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bebae3cd..77286fb7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -204,7 +205,6 @@ if (NOT USE_ENCLIB) else() set (USE_ENCLIB openssl) endif() - endif() set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") @@ -486,6 +486,14 @@ if (ENABLE_SOCK_CLOEXEC) add_definitions(-DENABLE_SOCK_CLOEXEC=1) endif() +if (ENABLE_NEW_RCVBUFFER) + add_definitions(-DENABLE_NEW_RCVBUFFER=1) + message(STATUS "RECEIVER_BUFFER: NEW") +else() + remove_definitions(-DENABLE_NEW_RCVBUFFER) + message(STATUS "RECEIVER_BUFFER: OLD") +endif() + if (CMAKE_MAJOR_VERSION LESS 3) set (FORCE_CXX_STANDARD_GNUONLY 1) endif() @@ -1291,6 +1299,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) endif() MafReadDir(test filelist.maf + HEADERS SOURCES_unittests SOURCES SOURCES_unittests ) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index d8c1d757a..3afce1cf3 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -192,6 +192,11 @@ will be removed when the problem is fixed globally. This option enables the standard C++ `thread` and `chrono` libraries (available since C++11) to be used by SRT instead of the `pthreads`. +**`--enable-new-rcvbuffer`** (default: ON) + +This option enables the new implementation of the receiver buffer with behavior and code improvements. +The new receiver buffer is to remain the only one. For the transition period there is a possibility to +fall back to the old one. **`--enable-profile`** (default: OFF) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 59399a4b3..8e18bd23e 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -151,8 +151,10 @@ 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()) return true; + if (m_UDT.m_bListening) return !m_QueuedSockets.empty(); @@ -2622,7 +2624,11 @@ void srt::CUDTUnited::checkBrokenSockets() // 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"). +#if ENABLE_NEW_RCVBUFFER + && s->core().m_pRcvBuffer->hasAvailablePackets()) +#else && s->core().m_pRcvBuffer->isRcvDataAvailable()) +#endif { const int bc = s->core().m_iBrokenCounter.load(); if (bc > 0) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 794066702..e861654eb 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -792,6 +792,8 @@ void CSndBuffer::increase() //////////////////////////////////////////////////////////////////////////////// +#if (!ENABLE_NEW_RCVBUFFER) + /* * RcvBuffer (circular buffer): * @@ -2289,4 +2291,6 @@ bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) return found; } +#endif // !ENABLE_NEW_RCVBUFFER + } // namespace srt diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 8756a49d1..428db748d 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -272,6 +272,8 @@ class CSndBuffer //////////////////////////////////////////////////////////////////////////////// +#if (!ENABLE_NEW_RCVBUFFER) + class CRcvBuffer { typedef sync::steady_clock::time_point time_point; @@ -562,6 +564,8 @@ class CRcvBuffer CRcvBuffer& operator=(const CRcvBuffer&); }; +#endif // !ENABLE_NEW_RCVBUFFER + } // namespace srt #endif diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp new file mode 100644 index 000000000..12e935057 --- /dev/null +++ b/srtcore/buffer_rcv.cpp @@ -0,0 +1,969 @@ +#if ENABLE_NEW_RCVBUFFER +#include +#include "buffer_rcv.h" +#include "logging.h" + +using namespace std; + +using namespace srt::sync; +using namespace srt_logging; +namespace srt_logging +{ + extern Logger brlog; +} +#define rbuflog brlog + +namespace srt { + +namespace { + struct ScopedLog + { + ScopedLog() {}; + + ~ScopedLog() + { + LOGC(rbuflog.Warn, log << ss.str()); + } + + stringstream ss; + }; + +#define IF_RCVBUF_DEBUG(instr) (void)0 + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const bool isOverrun = iLastPos < iStartPos; + + if (isOverrun) + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + } +} + + +/* + * RcvBufferNew (circular buffer): + * + * |<------------------- m_iSize ----------------------------->| + * | |<----------- m_iMaxPosInc ------------>| | + * | | | | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | | + * | |__last pkt received + * |___ m_iStartPos: first message to read + * + * m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped + * + * thread safety: + * m_iStartPos: CUDT::m_RecvLock + * m_iLastAckPos: CUDT::m_AckLock + * m_iMaxPosInc: none? (modified on add and ack + */ + +CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI) + : m_entries(size) + , m_szSize(size) // TODO: maybe just use m_entries.size() + , m_pUnitQueue(unitqueue) + , m_iStartSeqNo(initSeqNo) + , m_iStartPos(0) + , m_iFirstNonreadPos(0) + , m_iMaxPosInc(0) + , m_iNotch(0) + , m_numOutOfOrderPackets(0) + , m_iFirstReadableOutOfOrder(-1) + , m_bPeerRexmitFlag(peerRexmit) + , m_bMessageAPI(bMessageAPI) + , m_iBytesCount(0) + , m_iPktsCount(0) + , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) +{ + SRT_ASSERT(size < INT_MAX); // All position pointers are integers +} + +CRcvBufferNew::~CRcvBufferNew() +{ + for (size_t i = 0; i < m_szSize; ++i) + { + CUnit* unit = m_entries[i].pUnit; + if (unit != NULL) + { + m_pUnitQueue->makeUnitFree(unit); + m_entries[i].pUnit = NULL; + } + } +} + +int CRcvBufferNew::insert(CUnit* unit) +{ + SRT_ASSERT(unit != NULL); + const int32_t seqno = unit->m_Packet.getSeqNo(); + const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + + if (offset < 0) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); + return -2; + } + + if (offset >= (int)capacity()) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); + return -3; + } + + // TODO: Don't do assert here. Process this situation somehow. + // If >= 2, then probably there is a long gap, and buffer needs to be reset. + SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); + + const int pos = (m_iStartPos + offset) % m_szSize; + if (offset >= m_iMaxPosInc) + m_iMaxPosInc = offset + 1; + + // Packet already exists + SRT_ASSERT(pos >= 0 && pos < m_szSize); + if (m_entries[pos].status != EntryState_Empty) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); + return -1; + } + SRT_ASSERT(m_entries[pos].pUnit == NULL); + + m_pUnitQueue->makeUnitGood(unit); + m_entries[pos].pUnit = unit; + m_entries[pos].status = EntryState_Avail; + countBytes(1, (int)unit->m_Packet.getLength()); + + // If packet "in order" flag is zero, it can be read out of order. + // With TSBPD enabled packets are always assumed in order (the flag is ignored). + if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) + { + ++m_numOutOfOrderPackets; + onInsertNotInOrderPacket(pos); + } + + updateNonreadPos(); + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); + return 0; +} + +void CRcvBufferNew::dropUpTo(int32_t seqno) +{ + // Can drop only when nothing to read, and + // first unacknowledged packet is missing. + SRT_ASSERT(m_iStartPos == m_iFirstNonreadPos); + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); + + int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + SRT_ASSERT(len > 0); + if (len <= 0) + { + IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); + return; + } + + /*LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropUpTo(): seqno=" << seqno << ", pkts=" << len + << ". Buffer start " << m_iStartSeqNo << ".");*/ + + m_iMaxPosInc -= len; + if (m_iMaxPosInc < 0) + m_iMaxPosInc = 0; + + // Check that all packets being dropped are missing. + while (len > 0) + { + if (m_entries[m_iStartPos].pUnit != NULL) + { + releaseUnitInPos(m_iStartPos); + } + + if (m_entries[m_iStartPos].status != EntryState_Empty) + { + SRT_ASSERT(m_entries[m_iStartPos].status == EntryState_Drop || m_entries[m_iStartPos].status == EntryState_Read); + m_entries[m_iStartPos].status = EntryState_Empty; + } + + SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); + m_iStartPos = incPos(m_iStartPos); + --len; + } + + // Update positions + m_iStartSeqNo = seqno; + // Move forward if there are "read" entries. + releaseNextFillerEntries(); + // Set nonread position to the starting position before updating, + // because start position was increased, and preceeding packets are invalid. + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); +} + +void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) +{ + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); + // TODO: count bytes as removed? + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + if (msgno != 0) + { + for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + { + // TODO: Maybe check status? + if (!m_entries[i].pUnit) + continue; + + const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); + if (msgseq == msgno) + { + releaseUnitInPos(i); + m_entries[i].status = EntryState_Drop; + } + } + + return; + } + + // Drop by packet seqno range. + const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); + const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + if (offset_b < 0) + { + LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " + << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + return; + } + + const int start_off = max(0, offset_a); + const int last_pos = incPos(m_iStartPos, offset_b); + for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) + { + if (m_entries[i].pUnit) + { + releaseUnitInPos(i); + } + m_entries[i].status = EntryState_Drop; + } + + LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; " + << seqnohi << "]."); +} + +int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +{ + const bool canReadInOrder = hasReadableInorderPkts(); + if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + { + LOGC(rbuflog.Warn, log << "CRcvBufferNew.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); + return 0; + } + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); + + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + // Remember if we actually read out of order packet. + const bool readingOutOfOrderPacket = !canReadInOrder || m_iStartPos == m_iFirstReadableOutOfOrder; + + size_t remain = len; + char* dst = data; + int pkts_read = 0; + int bytes_extracted = 0; // The total number of bytes extracted from the buffer. + const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed + for (int i = readPos;; i = incPos(i)) + { + SRT_ASSERT(m_entries[i].pUnit); + if (!m_entries[i].pUnit) + { + LOGC(rbuflog.Error, log << "CRcvBufferNew::readMessage(): null packet encountered."); + break; + } + + const CPacket& packet = m_entries[i].pUnit->m_Packet; + const size_t pktsize = packet.getLength(); + const int32_t pktseqno = packet.getSeqNo(); + + // unitsize can be zero + const size_t unitsize = std::min(remain, pktsize); + memcpy(dst, packet.m_pcData, unitsize); + remain -= unitsize; + dst += unitsize; + + ++pkts_read; + bytes_extracted += (int) pktsize; + + if (m_tsbpd.isEnabled()) + updateTsbPdTimeBase(packet.getMsgTimeStamp()); + + if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) + --m_numOutOfOrderPackets; + + const bool pbLast = packet.getMsgBoundary() & PB_LAST; + if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) + { + msgctrl->pktseq = pktseqno; + msgctrl->msgno = packet.getMsgSeq(m_bPeerRexmitFlag); + } + if (msgctrl && pbLast) + { + msgctrl->srctime = count_microseconds(getPktTsbPdTime(packet.getMsgTimeStamp()).time_since_epoch()); + } + + releaseUnitInPos(i); + if (updateStartPos) + { + m_iStartPos = incPos(i); + --m_iMaxPosInc; + SRT_ASSERT(m_iMaxPosInc >= 0); + m_iStartSeqNo = CSeqNo::incseq(pktseqno); + } + else + { + // If out of order, only mark it read. + m_entries[i].status = EntryState_Read; + } + + if (pbLast) + { + if (readPos == m_iFirstReadableOutOfOrder) + m_iFirstReadableOutOfOrder = -1; + break; + } + } + + countBytes(-pkts_read, -bytes_extracted); + if (!m_tsbpd.isEnabled() && readingOutOfOrderPacket) + updateFirstReadableOutOfOrder(); + + releaseNextFillerEntries(); + + if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + { + m_iFirstNonreadPos = m_iStartPos; + //updateNonreadPos(); + } + + const int bytes_read = dst - data; + if (bytes_read < bytes_extracted) + { + LOGC(rbuflog.Error, log << "readMessage: small dst buffer, copied only " << bytes_read << "/" << bytes_extracted << " bytes."); + } + + return bytes_read; +} + +namespace { + /// @brief Writes bytes to file stream. + /// @param data pointer to data to write. + /// @param len the number of bytes to write + /// @param arg a void pointer to the fstream to write to. + /// @return true on success, false on failure + bool writeBytesToFile(char* data, int len, void* arg) + { + fstream* pofs = reinterpret_cast(arg); + pofs->write(data, len); + return !pofs->fail(); + } + + /// @brief Copies bytes to the destination buffer. + /// @param data pointer to data to copy. + /// @param len the number of bytes to copy + /// @param arg A pointer to the destination buffer + /// @return true on success, false on failure + bool copyBytesToBuf(char* data, int len, void* arg) + { + char* dst = reinterpret_cast(arg); + memcpy(dst, data, len); + return true; + } +} + +int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) +{ + int p = m_iStartPos; + const int end_pos = m_iFirstNonreadPos; + + const bool bTsbPdEnabled = m_tsbpd.isEnabled(); + const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); + + int rs = len; + while ((p != end_pos) && (rs > 0)) + { + if (!m_entries[p].pUnit) + { + p = incPos(p); + LOGC(rbuflog.Error, log << "readBufferTo: IPE: NULL unit found in file transmission"); + return -1; + } + + const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + + if (bTsbPdEnabled) + { + const steady_clock::time_point tsPlay = getPktTsbPdTime(pkt.getMsgTimeStamp()); + HLOGC(rbuflog.Debug, + log << "readBuffer: check if time to play:" + << " NOW=" << FormatTime(now) + << " PKT TS=" << FormatTime(tsPlay)); + + if ((tsPlay > now)) + break; /* too early for this unit, return whatever was copied */ + } + + const int pktlen = (int)pkt.getLength(); + const int remain_pktlen = pktlen - m_iNotch; + const int unitsize = std::min(remain_pktlen, rs); + + if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, arg)) + break; + + if (rs >= remain_pktlen) + { + releaseUnitInPos(p); + p = incPos(p); + m_iNotch = 0; + + m_iStartPos = p; + --m_iMaxPosInc; + SRT_ASSERT(m_iMaxPosInc >= 0); + m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + const int iBytesRead = len - rs; + /* we removed acked bytes form receive buffer */ + countBytes(-1, -iBytesRead); + + // Update positions + // Set nonread position to the starting position before updating, + // because start position was increased, and preceeding packets are invalid. + if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + { + m_iFirstNonreadPos = m_iStartPos; + } + + if (iBytesRead == 0) + { + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + } + + return iBytesRead; +} + +int CRcvBufferNew::readBuffer(char* dst, int len) +{ + return readBufferTo(len, copyBytesToBuf, reinterpret_cast(dst)); +} + +int CRcvBufferNew::readBufferToFile(fstream& ofs, int len) +{ + return readBufferTo(len, writeBytesToFile, reinterpret_cast(&ofs)); +} + +bool CRcvBufferNew::hasAvailablePackets() const +{ + return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); +} + +int CRcvBufferNew::getRcvDataSize() const +{ + if (m_iFirstNonreadPos >= m_iStartPos) + return m_iFirstNonreadPos - m_iStartPos; + + return m_szSize + m_iFirstNonreadPos - m_iStartPos; +} + +int CRcvBufferNew::getTimespan_ms() const +{ + if (!m_tsbpd.isEnabled()) + return 0; + + if (m_iMaxPosInc == 0) + return 0; + + const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); + int startpos = m_iStartPos; + + while (m_entries[startpos].pUnit == NULL) + { + if (startpos == lastpos) + break; + + ++startpos; + } + + if (m_entries[startpos].pUnit == NULL) + return 0; + + // Should not happen + SRT_ASSERT(m_entries[lastpos].pUnit != NULL); + if (m_entries[lastpos].pUnit == NULL) + return 0; + + const steady_clock::time_point startstamp = + getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + if (endstamp < startstamp) + return 0; + + // One millisecond is added as a duration of a packet in the buffer. + // If there is only one packet in the buffer, one millisecond is returned. + return count_milliseconds(endstamp - startstamp) + 1; +} + +int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const +{ + ScopedLock lck(m_BytesCountLock); + bytes = m_iBytesCount; + timespan = getTimespan_ms(); + return m_iPktsCount; +} + +CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const +{ + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + { + // TODO: Maybe check status? + if (!m_entries[i].pUnit) + continue; + + const CPacket& packet = m_entries[i].pUnit->m_Packet; + const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; + return info; + } + + const PacketInfo info = { -1, false, time_point() }; + return info; +} + +std::pair CRcvBufferNew::getAvailablePacketsRange() const +{ + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, countReadable()); + return std::pair(m_iStartSeqNo, seqno_last); +} + +size_t CRcvBufferNew::countReadable() const +{ + if (m_iFirstNonreadPos >= m_iStartPos) + return m_iFirstNonreadPos - m_iStartPos; + return m_szSize + m_iFirstNonreadPos - m_iStartPos; +} + +bool CRcvBufferNew::isRcvDataReady(time_point time_now) const +{ + const bool haveInorderPackets = hasReadableInorderPkts(); + if (!m_tsbpd.isEnabled()) + { + if (haveInorderPackets) + return true; + + SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); + return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + } + + if (!haveInorderPackets) + return false; + + const PacketInfo info = getFirstValidPacketInfo(); + + return info.tsbpd_time <= time_now; +} + +void CRcvBufferNew::countBytes(int pkts, int bytes) +{ + ScopedLock lock(m_BytesCountLock); + m_iBytesCount += bytes; // added or removed bytes from rcv buffer + m_iPktsCount += pkts; + if (bytes > 0) // Assuming one pkt when adding bytes + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); +} + +void CRcvBufferNew::releaseUnitInPos(int pos) +{ + CUnit* tmp = m_entries[pos].pUnit; + m_entries[pos] = Entry(); // pUnit = NULL; status = Empty + if (tmp != NULL) + m_pUnitQueue->makeUnitFree(tmp); +} + +void CRcvBufferNew::releaseNextFillerEntries() +{ + int pos = m_iStartPos; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) + { + m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + releaseUnitInPos(pos); + pos = incPos(pos); + m_iStartPos = pos; + } +} + +// TODO: Is this function complete? There are some comments left inside. +void CRcvBufferNew::updateNonreadPos() +{ + if (m_iMaxPosInc == 0) + return; + + // const PacketBoundary boundary = packet.getMsgBoundary(); + + //// The simplest case is when inserting a sequential PB_SOLO packet. + // if (boundary == PB_SOLO && (m_iFirstNonreadPos + 1) % m_szSize == pos) + //{ + // m_iFirstNonreadPos = pos; + // return; + //} + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + + int pos = m_iFirstNonreadPos; + while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST)) + { + // bool good = true; + + // look ahead for the whole message + + // We expect to see either of: + // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] + // [PB_SOLO] + // but not: + // [PB_FIRST] NULL ... + // [PB_FIRST] FREE/PASSACK/DROPPED... + // If the message didn't look as expected, interrupt this. + + // This begins with a message starting at m_iStartPos + // up to end_pos (excluding) OR until the PB_LAST message is found. + // If any of the units on this way isn't good, this OUTER loop + // will be interrupted. + for (int i = pos; i != end_pos; i = (i + 1) % m_szSize) + { + if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) + { + // good = false; + break; + } + + // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. + if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + { + m_iFirstNonreadPos = incPos(i); + break; + } + } + + if (pos == m_iFirstNonreadPos || !m_entries[m_iFirstNonreadPos].pUnit) + break; + + pos = m_iFirstNonreadPos; + } + + // 1. If there is a gap between this packet and m_iLastReadablePos + // then no sense to update m_iLastReadablePos. + + // 2. The simplest case is when this is the first sequential packet +} + +int CRcvBufferNew::findLastMessagePkt() +{ + for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + { + SRT_ASSERT(m_entries[i].pUnit); + + if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + { + return i; + } + } + + return -1; +} + +void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) +{ + if (m_numOutOfOrderPackets == 0) + return; + + // If the following condition is true, there is already a packet, + // that can be read out of order. We don't need to search for + // another one. The search should be done when that packet is read out from the buffer. + // + // There might happen that the packet being added precedes the previously found one. + // However, it is allowed to re bead out of order, so no need to update the position. + if (m_iFirstReadableOutOfOrder >= 0) + return; + + // Just a sanity check. This function is called when a new packet is added. + // So the should be unacknowledged packets. + SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_entries[insertPos].pUnit); + const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const PacketBoundary boundary = pkt.getMsgBoundary(); + + //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) + //{ + // // This packet can be read out of order + // m_iFirstReadableOutOfOrder = insertPos; + // return; + //} + + const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); + // First check last packet, because it is expected to be received last. + const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + if (!hasLast) + return; + + const int firstPktPos = (boundary & PB_FIRST) + ? insertPos + : scanNotInOrderMessageLeft(insertPos, msgNo); + if (firstPktPos < 0) + return; + + m_iFirstReadableOutOfOrder = firstPktPos; + return; +} + +void CRcvBufferNew::updateFirstReadableOutOfOrder() +{ + if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + return; + + if (m_iMaxPosInc == 0) + return; + + // TODO: unused variable outOfOrderPktsRemain? + int outOfOrderPktsRemain = m_numOutOfOrderPackets; + + // Search further packets to the right. + // First check if there are packets to the right. + const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + + int posFirst = -1; + int posLast = -1; + int msgNo = -1; + + for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + { + if (!m_entries[pos].pUnit) + { + posFirst = posLast = msgNo = -1; + continue; + } + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgOrderFlag()) // Skip in order packet + { + posFirst = posLast = msgNo = -1; + continue; + } + + --outOfOrderPktsRemain; + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_FIRST) + { + posFirst = pos; + msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); + } + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + posFirst = posLast = msgNo = -1; + continue; + } + + if (boundary & PB_LAST) + { + m_iFirstReadableOutOfOrder = posFirst; + return; + } + + if (pos == lastPos) + break; + } + + return; +} + +int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) const +{ + // Search further packets to the right. + // First check if there are packets to the right. + const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + if (startPos == lastPos) + return -1; + + int pos = startPos; + do + { + pos = incPos(pos); + if (!m_entries[pos].pUnit) + break; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); + return -1; + } + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_LAST) + return pos; + } while (pos != lastPos); + + return -1; +} + +int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +{ + // Search preceeding packets to the left. + // First check if there are packets to the left. + if (startPos == m_iStartPos) + return -1; + + int pos = startPos; + do + { + pos = decPos(pos); + + if (!m_entries[pos].pUnit) + return -1; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); + return -1; + } + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_FIRST) + return pos; + } while (pos != m_iStartPos); + + return -1; +} + +bool CRcvBufferNew::addRcvTsbPdDriftSample(uint32_t usTimestamp, int usRTTSample) +{ + return m_tsbpd.addDriftSample(usTimestamp, usRTTSample); +} + +void CRcvBufferNew::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) +{ + m_tsbpd.setTsbPdMode(timebase, wrap, delay); +} + +void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase, + bool wrp, + uint32_t delay, + const steady_clock::duration& udrift) +{ + m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); +} + +void CRcvBufferNew::applyGroupDrift(const steady_clock::time_point& timebase, + bool wrp, + const steady_clock::duration& udrift) +{ + m_tsbpd.applyGroupDrift(timebase, wrp, udrift); +} + +CRcvBufferNew::time_point CRcvBufferNew::getTsbPdTimeBase(uint32_t usPktTimestamp) const +{ + return m_tsbpd.getTsbPdTimeBase(usPktTimestamp); +} + +void CRcvBufferNew::updateTsbPdTimeBase(uint32_t usPktTimestamp) +{ + m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); +} + +string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +{ + stringstream ss; + + ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize; + ss << " pkts. "; + if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + { + ss << " (TSBPD ready in "; + if (m_entries[m_iStartPos].pUnit) + { + const uint32_t usPktTimestamp = m_entries[m_iStartPos].pUnit->m_Packet.getMsgTimeStamp(); + ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - tsNow); + } + else + { + ss << "n/a"; + } + + const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + if (m_entries[iLastPos].pUnit) + { + ss << ":"; + const uint32_t usPktTimestamp = m_entries[m_iStartPos].pUnit->m_Packet.getMsgTimeStamp(); + ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - tsNow); + ss << " ms"; + } + else + { + ss << ":n/a ms"; + } + } + + ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + return ss.str(); +} + +CRcvBufferNew::time_point CRcvBufferNew::getPktTsbPdTime(uint32_t usPktTimestamp) const +{ + return m_tsbpd.getPktTsbPdTime(usPktTimestamp); +} + +/* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan) +{ + // Average number of packets and timespan could be small, + // so rounding is beneficial, while for the number of + // bytes in the buffer is a higher value, so rounding can be omitted, + // but probably better to round all three values. + timespan = static_cast(round((m_mavg.timespan_ms()))); + bytes = static_cast(round((m_mavg.bytes()))); + return static_cast(round(m_mavg.pkts())); +} + +/* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now) +{ + if (!m_mavg.isTimeToUpdate(now)) + return; + + int bytes = 0; + int timespan_ms = 0; + const int pkts = getRcvDataSize(bytes, timespan_ms); + m_mavg.update(now, pkts, bytes, timespan_ms); +} + +} // namespace srt + +#endif // ENABLE_NEW_RCVBUFFER diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h new file mode 100644 index 000000000..dfc63d3f6 --- /dev/null +++ b/srtcore/buffer_rcv.h @@ -0,0 +1,343 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2020 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_BUFFER_RCV_H +#define INC_SRT_BUFFER_RCV_H + +#if ENABLE_NEW_RCVBUFFER + +#include "buffer.h" // AvgBufSize +#include "common.h" +#include "queue.h" +#include "sync.h" +#include "tsbpd_time.h" + +namespace srt +{ + +/* + * Circular receiver buffer. + * + * |<------------------- m_szSize ---------------------------->| + * | |<------------ m_iMaxPosInc ----------->| | + * | | | | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | | + * | \__last pkt received + * | + * \___ m_iStartPos: first message to read + * + * m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) + * + * thread safety: + * start_pos_: CUDT::m_RecvLock + * first_unack_pos_: CUDT::m_AckLock + * max_pos_inc_: none? (modified on add and ack + * first_nonread_pos_: + */ + +class CRcvBufferNew +{ + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + +public: + CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI); + + ~CRcvBufferNew(); + +public: + /// Insert a unit into the buffer. + /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + /// + /// @param [in] unit pointer to a data unit containing new packet + /// @param [in] offset offset from last ACK point. + /// + /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. + /// -3 if a packet is offset is ahead the buffer capacity. + // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. + int insert(CUnit* unit); + + /// 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 + /// + void dropUpTo(int32_t seqno); + + /// @brief Drop the whole message from the buffer. + /// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. + /// When one packet of the message is in the range of dropping, the whole message is to be dropped. + /// @param seqnolo sequence number of the first packet in the dropping range. + /// @param seqnohi sequence number of the last packet in the dropping range. + /// @param msgno message number to drop (0 if unknown) + void dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + + /// Read the whole message from one or several packets. + /// + /// @param [in,out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @param [in,out] message control data + /// + /// @return actual number of bytes extracted from the buffer. + /// 0 if nothing to read. + /// -1 on failure. + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + + /// Read acknowledged data into a user buffer. + /// @param [in, out] dst pointer to the target user buffer. + /// @param [in] len length of user buffer. + /// @return size of data read. -1 on error. + int readBuffer(char* dst, int len); + + /// Read acknowledged data directly into file. + /// @param [in] ofs C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. -1 on error. + int readBufferToFile(std::fstream& ofs, int len); + +public: + /// Get the starting position of the buffer as a packet sequence number. + int getStartSeqNo() const { return m_iStartSeqNo; } + + /// Given the sequence number of the first unacknowledged packet + /// tells the size of the buffer available for packets. + /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. + // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. + size_t getAvailSize(int iFirstUnackSeqNo) const + { + // Receiver buffer allows reading unacknowledged packets. + // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo + // then it does not have acknowledged packets and its full capacity is available. + // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. + const int iRBufSeqNo = getStartSeqNo(); + if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + { + // Full capacity is available, still don't want to encourage extra packets to come. + // Note: CSeqNo::seqlen(n, n) returns 1. + return capacity() - CSeqNo::seqlen(iFirstUnackSeqNo, iRBufSeqNo) + 1; + } + + // Note: CSeqNo::seqlen(n, n) returns 1. + return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; + } + + /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. + /// @return true if there are packets available for reading, false otherwise. + bool hasAvailablePackets() const; + + /// Query how many data has been continuously received (for reading) and available for reading out + /// regardless of the TSBPD. + /// TODO: Rename to countAvailablePackets(). + /// @return size of valid (continous) data for reading. + int getRcvDataSize() const; + + /// Get the number of packets, bytes and buffer timespan. + /// Differs from getRcvDataSize() that it counts all packets in the buffer, not only continious. + int getRcvDataSize(int& bytes, int& timespan) const; + + struct PacketInfo + { + int seqno; + bool seq_gap; //< true if there are missing packets in the buffer, preceding current packet + time_point tsbpd_time; + }; + + /// Get information on the 1st message in queue. + /// Similar to CRcvBuffer::getRcvFirstMsg + /// Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if + /// none + /// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) + /// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets. + /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true + /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: + /// IF skipseqno != -1, packet ready to play preceeded by missing packets.; + /// IF skipseqno == -1, no missing packet but 1st not ready to play. + PacketInfo getFirstValidPacketInfo() const; + + /// Get information on the packets available to be read + /// @returns a pair of sequence numbers + std::pair getAvailablePacketsRange() const; + + size_t countReadable() const; + + bool empty() const + { + return (m_iMaxPosInc == 0); + } + + /// 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. + /// TODO: Old receiver buffer capacity returns the actual size. Check for conflicts. + size_t capacity() const + { + return m_szSize - 1; + } + + int64_t getDrift() const { return m_tsbpd.drift(); } + + // TODO: make thread safe? + int debugGetSize() const + { + return getRcvDataSize(); + } + + /// Zero time to include all available packets. + /// TODO: Rename to 'canRead`. + bool isRcvDataReady(time_point time_now = time_point()) const; + + int getRcvAvgDataSize(int& bytes, int& timespan); + void updRcvAvgDataSize(const time_point& now); + + unsigned getRcvAvgPayloadSize() const { return m_uAvgPayloadSz; } + + void getInternalTimeBase(time_point& w_timebase, bool& w_wrp, duration& w_udrift) + { + return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift); + } + +public: // Used for testing + /// Peek unit in position of seqno + const CUnit* peek(int32_t seqno); + +private: + inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } + inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } + +private: + void countBytes(int pkts, int bytes); + void updateNonreadPos(); + void releaseUnitInPos(int pos); + + /// Release entries following the current buffer position if they were already + /// read out of order (EntryState_Read) or dropped (EntryState_Drop). + void releaseNextFillerEntries(); + + bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } + + /// Find position of the last packet of the message. + int findLastMessagePkt(); + + /// Scan for availability of out of order packets. + void onInsertNotInOrderPacket(int insertpos); + void updateFirstReadableOutOfOrder(); + int scanNotInOrderMessageRight(int startPos, int msgNo) const; + int scanNotInOrderMessageLeft(int startPos, int msgNo) const; + + typedef bool copy_to_dst_f(char* data, int len, void* arg); + + /// Read acknowledged data directly into file. + /// @param [in] ofs C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. + int readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg); + + /// @brief Estimate timespan of the stored packets (acknowledged and unacknowledged). + /// @return timespan in milliseconds + int getTimespan_ms() const; + +private: + // TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing. + // TODO: CUnitPtr is not in use at the moment, but may be a smart pointer. + // class CUnitPtr + // { + // public: + // void operator=(CUnit* pUnit) + // { + // if (m_pUnit != NULL) + // { + // // m_pUnitQueue->makeUnitFree(m_entries[i].pUnit); + // } + // m_pUnit = pUnit; + // } + // private: + // CUnit* m_pUnit; + // }; + + enum EntryStatus + { + EntryState_Empty, //< No CUnit record. + EntryState_Avail, //< Entry is available for reading. + EntryState_Read, //< Entry has already been read (out of order). + EntryState_Drop //< Entry has been dropped. + }; + struct Entry + { + Entry() + : pUnit(NULL) + , status(EntryState_Empty) + {} + + CUnit* pUnit; + EntryStatus status; + }; + + //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } + + FixedArray m_entries; + + const size_t m_szSize; // size of the array of units (buffer) + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartSeqNo; + int m_iStartPos; // the head position for I/O (inclusive) + int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + int m_iMaxPosInc; // the furthest data position + int m_iNotch; // the starting read point of the first unit + + size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false + int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to + // read + const bool m_bPeerRexmitFlag; // Needed to read message number correctly + const bool m_bMessageAPI; // Operation mode flag: message or stream. + +public: // TSBPD public functions + /// Set TimeStamp-Based Packet Delivery Rx Mode + /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay + /// @param [in] wrap Is in wrapping period + /// @param [in] delay aggreed TsbPD delay + /// + /// @return 0 + void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); + + void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); + + bool addRcvTsbPdDriftSample(uint32_t usTimestamp, int usRTTSample); + + time_point getPktTsbPdTime(uint32_t usPktTimestamp) const; + + time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; + void updateTsbPdTimeBase(uint32_t usPktTimestamp); + + /// Form a string of the current buffer fullness state. + /// number of packets acknowledged, TSBPD readiness, etc. + std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + +private: + CTsbpdTime m_tsbpd; + +private: // Statistics + AvgBufSize m_mavg; + + // TODO: m_BytesCountLock is probably not needed as the buffer has to be protected from simultaneous access. + mutable sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iPktsCount; // Number of payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation +}; + +} // namespace srt + +#endif // ENABLE_NEW_RCVBUFFER +#endif // INC_SRT_BUFFER_RCV_H diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 787533cea..67c2e6b08 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5166,6 +5166,199 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e * This thread runs only if TsbPd mode is enabled * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay. */ +#if ENABLE_NEW_RCVBUFFER +void * srt::CUDT::tsbpd(void* param) +{ + CUDT* self = (CUDT*)param; + + THREAD_STATE_INIT("SRT:TsbPd"); + +#if ENABLE_EXPERIMENTAL_BONDING + // Make the TSBPD thread a "client" of the group, + // which will ensure that the group will not be physically + // deleted until this thread exits. + // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!! + CUDTUnited::GroupKeeper gkeeper(self->uglobal(), self->m_parent); +#endif + + UniqueLock recv_lock(self->m_RecvLock); + CSync recvdata_cc(self->m_RecvDataCond, recv_lock); + CSync tsbpd_cc(self->m_RcvTsbPdCond, recv_lock); + + self->m_bTsbPdAckWakeup = true; + while (!self->m_bClosing) + { + steady_clock::time_point tsNextDelivery; // Next packet delivery time + bool rxready = false; +#if ENABLE_EXPERIMENTAL_BONDING + bool shall_update_group = false; +#endif + + enterCS(self->m_RcvBufferLock); + const steady_clock::time_point tnow = steady_clock::now(); + + self->m_pRcvBuffer->updRcvAvgDataSize(tnow); + const srt::CRcvBufferNew::PacketInfo info = self->m_pRcvBuffer->getFirstValidPacketInfo(); + + const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); + tsNextDelivery = info.tsbpd_time; + + if (!self->m_bTLPktDrop) + { + rxready = !info.seq_gap && is_time_to_deliver; + } + else if (is_time_to_deliver) + { + rxready = true; + if (info.seq_gap) + { + const int seq_gap_len = CSeqNo::seqoff(self->m_iRcvLastSkipAck, info.seqno); + SRT_ASSERT(seq_gap_len > 0); + /*if (!info.seq_gap) + { + LOGC(brlog.Warn, log << "TSBPD worker: no gap. pktseqno=" << info.seqno + << ", m_iRcvLastSkipAck=" << self->m_iRcvLastSkipAck + << ", RBuffer start seqno=" << self->m_pRcvBuffer->getStartSeqNo() + << ", m_iRcvLastAck=" << self->m_iRcvLastAck + << ", init seqnoo=" << self->m_iISN); + }*/ + + // Drop too late packets + self->updateForgotten(seq_gap_len, self->m_iRcvLastSkipAck, info.seqno); + //if (info.seq_gap) // If there is no sequence gap, we are reading ahead of ACK. + //{ + self->m_pRcvBuffer->dropUpTo(info.seqno); + //} + + self->m_iRcvLastSkipAck = info.seqno; +#if ENABLE_EXPERIMENTAL_BONDING + shall_update_group = true; +#endif + +#if ENABLE_LOGGING + const int64_t timediff_us = count_microseconds(tnow - info.tsbpd_time); + // TODO: seq_gap_len is not the actual number of packets dropped. +#if ENABLE_HEAVY_LOGGING + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" + << seq_gap_len << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " + << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); +#endif + LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << seq_gap_len << " packet(s), packet seqno %" << info.seqno + << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') + << (timediff_us % 1000) << " ms"); +#endif + + tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. + } + } + leaveCS(self->m_RcvBufferLock); + + if (rxready) + { + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " + << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + /* + * There are packets ready to be delivered + * signal a waiting "recv" call if there is any data available + */ + if (self->m_config.bSynRecving) + { + recvdata_cc.signal_locked(recv_lock); + } + /* + * Set EPOLL_IN to wakeup any thread waiting on epoll + */ + self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); +#if ENABLE_EXPERIMENTAL_BONDING + // If this is NULL, it means: + // - the socket never was a group member + // - the socket was a group member, but: + // - was just removed as a part of closure + // - and will never be member of the group anymore + + // If this is not NULL, it means: + // - This socket is currently member of the group + // - This socket WAS a member of the group, though possibly removed from it already, BUT: + // - the group that this socket IS OR WAS member of is in the GroupKeeper + // - the GroupKeeper prevents the group from being deleted + // - it is then completely safe to access the group here, + // EVEN IF THE SOCKET THAT WAS ITS MEMBER IS BEING DELETED. + + // It is ensured that the group object exists here because GroupKeeper + // keeps it busy, even if you just closed the socket, remove it as a member + // or even the group is empty and was explicitly closed. + if (gkeeper.group) + { + // Functions called below will lock m_GroupLock, which in hierarchy + // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock + // m_GroupLock inside the calls. + InvertedLock unrecv(self->m_RecvLock); + // The current "APP reader" needs to simply decide as to whether + // the next CUDTGroup::recv() call should return with no blocking or not. + // When the group is read-ready, it should update its pollers as it sees fit. + + // NOTE: this call will set lock to m_IncludedGroup->m_GroupLock + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: GROUP: checking if %" << info.seqno << " makes group readable"); + gkeeper.group->updateReadState(self->m_SocketID, info.seqno); + + if (shall_update_group) + { + // A group may need to update the parallelly used idle links, + // should it have any. Pass the current socket position in order + // to skip it from the group loop. + // NOTE: SELF LOCKING. + gkeeper.group->updateLatestRcv(self->m_parent); + } + } +#endif + CGlobEvent::triggerEvent(); + tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. + } + + if (!is_zero(tsNextDelivery)) + { + IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); + /* + * Buffer at head of queue is not ready to play. + * Schedule wakeup when it will be. + */ + self->m_bTsbPdAckWakeup = false; + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + THREAD_PAUSED(); + tsbpd_cc.wait_until(tsNextDelivery); + THREAD_RESUMED(); + } + else + { + /* + * We have just signaled epoll; or + * receive queue is empty; or + * next buffer to deliver is not in receive queue (missing packet in sequence). + * + * Block until woken up by one of the following event: + * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time + * if any) + * - New buffers ACKed + * - Closing the connection + */ + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); + self->m_bTsbPdAckWakeup = true; + THREAD_PAUSED(); + tsbpd_cc.wait(); + THREAD_RESUMED(); + } + + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + } + THREAD_EXIT(); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); + return NULL; +} +#else void * srt::CUDT::tsbpd(void *param) { CUDT *self = (CUDT *)param; @@ -5382,6 +5575,7 @@ void * srt::CUDT::tsbpd(void *param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); return NULL; } +#endif // ENABLE_NEW_RCVBUFFER void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { @@ -5435,7 +5629,12 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd try { m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); +#if ENABLE_NEW_RCVBUFFER + SRT_ASSERT(m_iISN != -1); + m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_bPeerRexmitFlag, m_config.bMessageAPI); +#else m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.iRcvBufSize); +#endif // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); m_pRcvLossList = new CRcvLossList(m_config.iFlightFlagSize); @@ -6609,18 +6808,26 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) size_t srt::CUDT::getAvailRcvBufferSizeLock() const { ScopedLock lck(m_RcvBufferLock); - return m_pRcvBuffer->getAvailBufSize(); + return getAvailRcvBufferSizeNoLock(); } size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const { +#if ENABLE_NEW_RCVBUFFER + return m_pRcvBuffer->getAvailSize(m_iRcvLastAck); +#else return m_pRcvBuffer->getAvailBufSize(); +#endif } bool srt::CUDT::isRcvBufferReady() const { ScopedLock lck(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); +#else return m_pRcvBuffer->isRcvDataReady(); +#endif } // int by_exception: accepts values of CUDTUnited::ErrorHandling: @@ -6663,7 +6870,13 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality"); enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) + : 0; +#else const int res = m_pRcvBuffer->readMsg(data, len); +#endif leaveCS(m_RcvBufferLock); w_mctrl.srctime = 0; @@ -6697,13 +6910,21 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return res; } +#if !ENABLE_NEW_RCVBUFFER const int seqdistance = -1; +#endif if (!m_config.bSynRecving) { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len); enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) + : 0; +#else const int res = m_pRcvBuffer->readMsg(data, len, (w_mctrl), seqdistance); +#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (NON-BLOCKING) result=" << res); @@ -6765,9 +6986,13 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ do { +#if ENABLE_NEW_RCVBUFFER + if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now())) +#else steady_clock::time_point tstime SRT_ATR_UNUSED; int32_t seqno; if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady((tstime), (seqno), seqdistance)) +#endif { /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -6780,7 +7005,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ // of kicking TSBPD. // bool spurious = (tstime != 0); - HLOGC(tslog.Debug, log << CONID() << "receiveMessage: KICK tsbpd" << (is_zero(tstime) ? " (SPURIOUS!)" : "")); + HLOGC(tslog.Debug, log << CONID() << "receiveMessage: KICK tsbpd"); tscond.signal_locked(recvguard); } @@ -6823,7 +7048,11 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ */ enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + res = m_pRcvBuffer->readMessage((data), len, &w_mctrl); +#else res = m_pRcvBuffer->readMsg((data), len, (w_mctrl), seqdistance); +#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (BLOCKING) result=" << res); @@ -7509,12 +7738,22 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) { const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); - HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << ack << " vs. current %" << m_iRcvLastSkipAck - << " (signing off " << acksize << " packets)"); + HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << m_iRcvLastSkipAck << " -> %" << ack + << " (" << acksize << " packets)"); m_iRcvLastAck = ack; m_iRcvLastSkipAck = ack; +#if ENABLE_NEW_RCVBUFFER + const std::pair range = m_pRcvBuffer->getAvailablePacketsRange(); + // Some packets acknowledged are not available in the buffer. + if (CSeqNo::seqcmp(range.second, ack) < 0) + { + LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first + << " - %" << range.second); + } + return acksize; +#else // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. @@ -7529,6 +7768,7 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) if (distance > 0) return CSeqNo::decseq(ack, distance); return ack; +#endif } namespace srt { @@ -8644,10 +8884,16 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; + { - const bool using_rexmit_flag = m_bPeerRexmitFlag; UniqueLock rlock(m_RecvLock); + const bool using_rexmit_flag = m_bPeerRexmitFlag; +#if ENABLE_NEW_RCVBUFFER + m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); +#else m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); +#endif // When the drop request was received, it means that there are // packets for which there will never be ACK sent; if the TSBPD thread // is currently in the ACK-waiting state, it may never exit. @@ -8659,8 +8905,6 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } - const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; - dropFromLossLists(dropdata[0], dropdata[1]); // move forward with current recv seq no. @@ -8809,7 +9053,11 @@ void srt::CUDT::updateSrtRcvSettings() { /* We are TsbPd receiver */ enterCS(m_RecvLock); +#if ENABLE_NEW_RCVBUFFER + m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms)); +#else m_pRcvBuffer->setRcvTsbPdMode(m_tsRcvPeerStartTime, milliseconds_from(m_iTsbPdDelay_ms)); +#endif leaveCS(m_RecvLock); HLOGF(cnlog.Debug, @@ -9718,17 +9966,28 @@ int srt::CUDT::processData(CUnit* in_unit) } else { +#if ENABLE_NEW_RCVBUFFER + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); +#else LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo << ", insert offset " << offset << ". " << m_pRcvBuffer->strFullnessState(steady_clock::now()) ); +#endif return -1; } } bool adding_successful = true; +#if ENABLE_NEW_RCVBUFFER + if (m_pRcvBuffer->insert(*i) < 0) +#else if (m_pRcvBuffer->addData(*i, offset) < 0) +#endif { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. // So this packet is "redundant". @@ -9774,14 +10033,6 @@ int srt::CUDT::processData(CUnit* in_unit) } #if ENABLE_HEAVY_LOGGING - std::ostringstream timebufspec; - if (m_bTsbPd) - { - int dsize = m_pRcvBuffer->getRcvDataSize(); - timebufspec << "(" << FormatTime(m_pRcvBuffer->debugGetDeliveryTime(0)) - << "-" << FormatTime(m_pRcvBuffer->debugGetDeliveryTime(dsize-1)) << ")"; - } - std::ostringstream expectspec; if (excessive) expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; @@ -9798,7 +10049,6 @@ int srt::CUDT::processData(CUnit* in_unit) << ") " << " RSL=" << expectspec.str() << " SN=" << rexmitstat[pktrexmitflag] - << " DLVTM=" << timebufspec.str() << " FLAGS: " << rpkt.MessageFlagStr()); #endif diff --git a/srtcore/core.h b/srtcore/core.h index 523118fce..9f957d040 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -60,6 +60,7 @@ modified by #include "common.h" #include "list.h" #include "buffer.h" +#include "buffer_rcv.h" #include "window.h" #include "packet.h" #include "channel.h" @@ -417,7 +418,11 @@ class CUDT SRTU_PROPERTY_RO(SRTSOCKET, id, m_SocketID); SRTU_PROPERTY_RO(bool, isClosing, m_bClosing); +#if ENABLE_NEW_RCVBUFFER + SRTU_PROPERTY_RO(srt::CRcvBufferNew*, rcvBuffer, m_pRcvBuffer); +#else SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); +#endif SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond); @@ -867,7 +872,11 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data +#if ENABLE_NEW_RCVBUFFER + CRcvBufferNew* m_pRcvBuffer; //< Receiver buffer +#else CRcvBuffer* m_pRcvBuffer; //< Receiver buffer +#endif CRcvLossList* m_pRcvLossList; //< Receiver loss list std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. int m_iReorderTolerance; //< Current value of dynamic reorder tolerance diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 54e15df62..16fb48d45 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -3,6 +3,7 @@ SOURCES api.cpp buffer.cpp +buffer_rcv.cpp cache.cpp channel.cpp common.cpp @@ -53,6 +54,7 @@ udt.h PRIVATE HEADERS api.h buffer.h +buffer_rcv.h cache.h channel.h common.h diff --git a/srtcore/queue.h b/srtcore/queue.h index 2b7408747..21983d005 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -79,6 +79,7 @@ struct CUnit DROPPED = 3 }; Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped + // TODO: Transition to the new RcvBuffer allows to use bool here. }; class CUnitQueue diff --git a/srtcore/utilities.h b/srtcore/utilities.h index f787a579e..1610505b8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -410,6 +410,70 @@ struct DynamicStruct }; +/// Fixed-size array template class. +namespace srt { + +template +class FixedArray +{ +public: + FixedArray(size_t size) + : m_size(size) + , m_entries(new T[size]) + { + } + + ~FixedArray() + { + delete [] m_entries; + } + +public: + const T& operator[](size_t index) const + { + if (index >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + T& operator[](size_t index) + { + if (index >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + const T& operator[](int index) const + { + if (index < 0 || static_cast(index) >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + T& operator[](int index) + { + if (index < 0 || static_cast(index) >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + size_t size() const { return m_size; } + +private: + FixedArray(const FixedArray& ); + FixedArray& operator=(const FixedArray&); + +private: + size_t m_size; + T* const m_entries; +}; + +} // namespace srt + // ------------------------------------------------------------ From 427ceceff868921c0ebaba07101c55b5e1221b02 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 22 Oct 2021 14:04:27 +0200 Subject: [PATCH 297/790] [tests] Receiver buffer tests --- test/test_buffer.cpp | 430 ++++++++++++++++++++++++++++++++++++++++++- test/test_epoll.cpp | 2 +- 2 files changed, 422 insertions(+), 10 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 851fe1167..896303d59 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -2,6 +2,7 @@ #include #include "gtest/gtest.h" #include "buffer.h" +#include "buffer_rcv.h" using namespace srt; using namespace std; @@ -28,7 +29,14 @@ class CRcvBufferReadMsg m_unit_queue = unique_ptr(new CUnitQueue); ASSERT_NE(m_unit_queue.get(), nullptr); m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + +#if ENABLE_NEW_RCVBUFFER + const bool enable_msg_api = true; + const bool enable_peer_rexmit = true; + m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); +#else m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); +#endif ASSERT_NE(m_rcv_buffer.get(), nullptr); } @@ -36,8 +44,8 @@ class CRcvBufferReadMsg { // Code here will be called just after the test completes. // OK to throw exceptions from here if needed. - m_unit_queue.reset(); m_rcv_buffer.reset(); + m_unit_queue.reset(); } public: @@ -68,8 +76,12 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->insert(unit); +#else const int offset = CSeqNo::seqoff(m_first_unack_seqno, seqno); return m_rcv_buffer->addData(unit, offset); +#endif } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed @@ -104,31 +116,54 @@ class CRcvBufferReadMsg int ackPackets(int num_pkts) { m_first_unack_seqno = CSeqNo::incseq(m_first_unack_seqno, num_pkts); +#if ENABLE_NEW_RCVBUFFER + return 0; +#else return m_rcv_buffer->ackData(num_pkts); +#endif } int getAvailBufferSize() { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->getAvailSize(m_first_unack_seqno); +#else return m_rcv_buffer->getAvailBufSize(); +#endif } int readMessage(char* data, size_t len) { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->readMessage(data, len); +#else return m_rcv_buffer->readMsg(data, len); +#endif } bool hasAvailablePackets() { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->hasAvailablePackets(); +#else return m_rcv_buffer->isRcvDataAvailable(); +#endif } protected: unique_ptr m_unit_queue; +#if ENABLE_NEW_RCVBUFFER + unique_ptr m_rcv_buffer; +#else unique_ptr m_rcv_buffer; +#endif const int m_buff_size_pkts = 16; const int m_init_seqno = 1000; int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; + + const sync::steady_clock::time_point m_tsbpd_base = sync::steady_clock::now(); // now() - HS.timestamp, microseconds + const sync::steady_clock::duration m_delay = sync::milliseconds_from(200); }; // Check the available size of the receiver buffer. @@ -137,6 +172,19 @@ TEST_F(CRcvBufferReadMsg, Create) EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); } +// Check that destroying the buffer also frees memory units. +TEST_F(CRcvBufferReadMsg, Destroy) +{ + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < getAvailBufferSize(); ++i) + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + + m_rcv_buffer.reset(); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + // Fill the buffer full, and check adding more data results in an error. TEST_F(CRcvBufferReadMsg, FullBuffer) { @@ -163,9 +211,11 @@ TEST_F(CRcvBufferReadMsg, FullBuffer) EXPECT_TRUE(size_t(res) == m_payload_sz); EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); } + + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } -// BUG in the new RCV buffer!!! +// BUG in the old RCV buffer!!! // In this test case a packet is added to receiver buffer with offset 1, // thus leaving offset 0 with an empty pointer. // The buffer says it is not empty, and the data is available @@ -187,42 +237,112 @@ TEST_F(CRcvBufferReadMsg, OnePacketGap) EXPECT_EQ(res, 0); // BUG. Acknowledging an empty position must not result in a read-readiness. + // TODO: Actually we should not acknowledge, but must drop instead. ackPackets(1); - // Wrong behavior (BUG) +#if ENABLE_NEW_RCVBUFFER // Expected behavior + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); + + const auto next_packet = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(next_packet.seqno, m_init_seqno + 1); +#else // Wrong behavior (BUG) EXPECT_TRUE(hasAvailablePackets()); EXPECT_TRUE(rcv_buffer.isRcvDataReady()); +#endif EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 2); +#if ENABLE_NEW_RCVBUFFER + // The new buffer will return 0 as reading is not available. + res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_EQ(res, 0); +#else cerr << "Expecting IPE from readBuffer(..): \n"; res = rcv_buffer.readBuffer(buff.data(), buff.size()); EXPECT_EQ(res, -1); +#endif res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); + +#if ENABLE_NEW_RCVBUFFER + // Add a missing packet (can't add before an acknowledged position in the old buffer). + EXPECT_EQ(addMessage(1, m_init_seqno), 0); + + for (int pktno = 0; pktno < 2; ++pktno) + { + const size_t msg_bytelen = m_payload_sz; + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); + EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), msg_bytelen, CSeqNo::incseq(m_init_seqno, pktno))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#endif + + // Further read is not possible + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); +} + +/// One packet is added to the buffer after 1-packet gap. Should be read only after ACK. +/// 1. insert (1) +/// | +/// +---+---+ ---+---+---+---+ +---+ +/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+---+ ---+---+---+---+ +---+ +/// 2. drop (0) +/// 2. read (1) +/// +TEST_F(CRcvBufferReadMsg, OnePacketGapDrop) +{ + // Add one packet message to to the buffer + // with a gap of one packet. + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0); + auto& rcv_buffer = *m_rcv_buffer.get(); + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); +#if ENABLE_NEW_RCVBUFFER + rcv_buffer.dropUpTo(CSeqNo::incseq(m_init_seqno)); +#else + rcv_buffer.dropData(1); +#endif + + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); + array buff; + EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, CSeqNo::incseq(m_init_seqno))); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Add one packet to the buffer and read it once it is acknowledged. // Confirm the data read is valid. +// Don't allow to add packet with the same sequence number. TEST_F(CRcvBufferReadMsg, OnePacket) { const size_t msg_pkts = 1; // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno, false); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0); + // Adding a packet into the same position must return an error. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -1); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; + // The new receiver buffer allows reading without ACK. +#if !ENABLE_NEW_RCVBUFFER EXPECT_FALSE(hasAvailablePackets()); + const int res1 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res1, 0); // Full ACK ackPackets(msg_pkts); +#endif EXPECT_TRUE(hasAvailablePackets()); const int res2 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res2, msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Add ten packets to the buffer, acknowledge and read some of them. @@ -243,7 +363,12 @@ TEST_F(CRcvBufferReadMsg, AddData) // The value is reported by SRT receiver like this: // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); +#if ENABLE_NEW_RCVBUFFER + // The new receiver buffer does not need ACK to allow reading. + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(hasAvailablePackets()); +#endif // Now acknowledge two packets const int ack_pkts = 2; @@ -261,11 +386,24 @@ TEST_F(CRcvBufferReadMsg, AddData) } // Add packet to the position of oackets already read. - // Can't check, as negative offset is an error not handled by the receiver buffer. - //EXPECT_EQ(addPacket(m_init_seqno), -1); + // Can't check the old buffer, as it does not handle a negative offset. +#if ENABLE_NEW_RCVBUFFER + EXPECT_EQ(addPacket(m_init_seqno), -2); +#endif // Add packet to a non-empty position. EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); + + const int num_pkts_left = num_pkts - ack_pkts; + ackPackets(num_pkts_left); + for (int i = 0; i < num_pkts_left; ++i) + { + const int res = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Check reading the whole message (consisting of several packets) from the buffer. @@ -291,6 +429,7 @@ TEST_F(CRcvBufferReadMsg, MsgAcked) const ptrdiff_t offset = i * m_payload_sz; EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Check reading the whole message (consisting of several packets) into @@ -320,6 +459,7 @@ TEST_F(CRcvBufferReadMsg, SmallReadBuffer) EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // BUG!!! @@ -336,6 +476,19 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) // Nothing to read (0 for zero bytes read). const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; +#if ENABLE_NEW_RCVBUFFER + // The new receiver buffer does not care about ACK. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); @@ -344,14 +497,24 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) // ACK half of the message and check read-readiness. ackPackets(2); // FIXME: Sadly RCV buffer says the data is ready to be read. - // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - // EXPECT_FALSE(hasAvailablePackets()); EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); EXPECT_TRUE(hasAvailablePackets()); // Actually must be nothing to read (can't read half a message). const int res2 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res2, 0); + + // ACK the remaining half of the message and check read-readiness. + ackPackets(2); + const int res3 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res3, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +#endif + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // BUG!!! @@ -363,12 +526,39 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, m_init_seqno, true); +#if ENABLE_NEW_RCVBUFFER + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); +#endif const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + +#if ENABLE_NEW_RCVBUFFER + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#else + ackPackets(msg_pkts); + // The old buffer still does not free units. + EXPECT_NE(m_unit_queue->size(), m_unit_queue->capacity()); + // BUG: wrong read-ready state. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + // Nothing read, but empty units are freed. + EXPECT_EQ(readMessage(buff.data(), buff.size()), 0); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#endif } // Adding a message with the out-of-order flag set. @@ -379,8 +569,13 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, CSeqNo::incseq(m_init_seqno, 1), true); +#if ENABLE_NEW_RCVBUFFER + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); +#endif const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); @@ -407,10 +602,15 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) EXPECT_TRUE(res3 == m_payload_sz); EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, m_init_seqno)); - // Only "passack" packets remain in the buffer. + // Only "passack" or EntryState_Read packets remain in the buffer. // They are falsely signalled as read-ready. +#if ENABLE_NEW_RCVBUFFER + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); +#else EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); // BUG: nothing to read. EXPECT_TRUE(hasAvailablePackets()); // BUG: nothing to read. +#endif // Adding a packet right after the EntryState_Read packets. const int seqno = CSeqNo::incseq(m_init_seqno, msg_pkts + 1); @@ -422,4 +622,216 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +// One message (4 packets) are added to the buffer. +// Check if reading is only possible once the whole message is present in the buffer. +TEST_F(CRcvBufferReadMsg, LongMsgReadReady) +{ + const size_t msg_pkts = 4; + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + for (size_t i = 0; i < msg_pkts; ++i) + { + // int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) + const bool pb_first = (i == 0); + const bool pb_last = (i == (msg_pkts - 1)); + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), pb_first, pb_last), 0); + ackPackets(1); + if (!pb_last) + { +#if ENABLE_NEW_RCVBUFFER + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); +#else + // BUG: The old buffer returns true (read-readiness). + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#endif + EXPECT_EQ(readMessage(buff.data(), buff.size()), 0); + } + } + + // Read the whole message. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +#if ENABLE_NEW_RCVBUFFER +// One message (4 packets) is added to the buffer. Can be read out of order. +// Reading should be possible even before the missing packet is dropped. +TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) +{ + const size_t msg_pkts = 4; + // 1. Add one message (4 packets) without acknowledging + const int msg_seqno = m_init_seqno + 1; // seqno of the first packet in the message + EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), 0); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + + // 2. Read full message after gap. + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + int res = m_rcv_buffer->readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, msg_seqno + i)); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + + // Can't add to the same message + EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), -1); + + const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(pkt_info.seqno, -1); // Nothing to read + EXPECT_TRUE(srt::sync::is_zero(pkt_info.tsbpd_time)); + + // Drop missing packet + m_rcv_buffer->dropUpTo(msg_seqno); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + // All memory units are expected to be freed. + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +// One message (4 packets) is added to the buffer after a message with "in order" flag. +// Read in order +TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) +{ + const size_t msg_pkts = 4; + // 1. Add one packet with inOrder=true and one message (4 packets) with inOrder=false + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + 2 * msg_pkts, true), 0); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + msg_pkts, true), 0); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + + // 2. Read messages in order + const size_t msg_bytelen = msg_pkts * m_payload_sz; + std::array buff; + for (int msg_i = 0; msg_i < 3; ++msg_i) + { + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, m_init_seqno + msg_i * msg_pkts + i)); + } + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); } + +/// One packet is added to the buffer. Can be read on TSBPD-readiness. +/// +/// 1. insert +/// | +/// +---+ ---+---+---+---+---+ +---+ +/// | 1 | 0 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+ ---+---+---+---+---+ +---+ +/// | +/// 2. read +/// +TEST_F(CRcvBufferReadMsg, OnePacketTSBPD) +{ + const size_t msg_pkts = 1; + + m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay); + + const int packet_ts = 0; + // Adding one message. Note that all packets have the out of order flag + // set to false by default in TSBPD mode, but this flag is ignored. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), 0); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Confirm adding to the same location returns an error. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), -1); + + // There is one packet in the buffer, but not ready to read after delay/2 + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + (m_delay / 2))); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay - sync::microseconds_from(1))); + // There is one packet in the buffer ready to read after delay + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay + sync::microseconds_from(1))); + + // Read out the first message + const int read_len = m_rcv_buffer->readMessage(buff.data(), buff.size()); + EXPECT_EQ(read_len, msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), read_len, m_init_seqno)); + + // Check the state after a packet was read + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -2); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); +} + +/// TSBPD = ON, a ready to play packet is preceeded by a missing packet. +/// The read-rediness must be signalled, and a packet must be read after the missing +/// one is dropped. +/// The TSBPD delay is set to 200 ms. This means, that the packet can be played +/// not earlier than after 200200 microseconds from the peer start time. +/// The peer start time is set to 100000 us. +/// +/// +/// || +/// | / +/// | / +/// | | +/// +---+---+---+---+---+---+ +---+ +/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+---+---+---+---+---+ +---+ +/// | | +/// | \__last pkt received +/// | +/// \___ m_iStartPos: first message to read +/// \___ m_iLastAckPos: last ack sent +/// +/// m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped +/// +TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) +{ + m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay); + // Add a solo packet to position m_init_seqno + 1 with timestamp 200 us + const int seqno = m_init_seqno + 1; + const int32_t pkt_ts = 200; + EXPECT_EQ(addMessage(1, seqno, false, pkt_ts), 0); + + const auto readready_timestamp = m_tsbpd_base + sync::microseconds_from(pkt_ts) + m_delay; + // Check that getFirstValidPacketInfo() returns first valid packet. + const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(pkt_info.tsbpd_time, readready_timestamp); + EXPECT_EQ(pkt_info.seqno, seqno); + EXPECT_TRUE(pkt_info.seq_gap); + + // The packet can't be read because there is a missing packet preceeding. + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(readready_timestamp)); + + const int seq_gap_len = CSeqNo::seqoff(m_rcv_buffer->getStartSeqNo(), pkt_info.seqno); + EXPECT_GT(seq_gap_len, 0); + if (seq_gap_len > 0) + { + m_rcv_buffer->dropUpTo(pkt_info.seqno); + } + + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(readready_timestamp)); + + const size_t msg_bytelen = m_payload_sz; + array buff; + EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +#endif // ENABEL_NEW_RCVBUFFER diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 8fb36be53..4644e3267 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -718,7 +718,7 @@ class TestEPoll: public testing::Test 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], acpsock); // read event is for bind socket + ASSERT_EQ(read[0], acpsock); // read event is for bind socket } char buffer[1316]; From f11b026d8efeb25195869bd1f77d757979a17150 Mon Sep 17 00:00:00 2001 From: cg82616424 Date: Tue, 26 Oct 2021 16:17:41 +0800 Subject: [PATCH 298/790] [core] fix listener's cookie check (#2176). If a cookie from the caller HS conclusion response does not match the current listener's cookie, check the one that would have been generated a minute ago by the listener. --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 67c2e6b08..d0849c6b8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10610,7 +10610,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co clientport, sizeof(clientport), NI_NUMERICHOST | NI_NUMERICSERV); - int64_t timestamp = (count_microseconds(steady_clock::now() - m_stats.tsStartTime) / 60000000) + distractor - + int64_t timestamp = (count_microseconds(steady_clock::now() - m_stats.tsStartTime) / 60000000) + distractor + correction; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; From d9b7988347fdbd4c9e1ae7fdc3b0bb3080aa8ae1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 27 Oct 2021 02:42:05 -0500 Subject: [PATCH 299/790] [build] Add ShowProjectConfig CMake Module. (#2161) New build option ENABLE_SHOW_PROJECT_CONFIG. --- CMakeLists.txt | 6 + docs/build/build-options.md | 6 + scripts/ShowProjectConfig.cmake | 190 ++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 scripts/ShowProjectConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 77286fb7a..e48798713 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF) option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -1340,3 +1341,8 @@ if (DEFINED SRT_EXTRA_APPS_INC) # No extra variables expected. Just use the variables # already provided and define additional targets. endif() + +if (ENABLE_SHOW_PROJECT_CONFIG) + include(ShowProjectConfig) + ShowProjectConfig() +endif() diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 3afce1cf3..cdac4c068 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -52,6 +52,12 @@ All options below are presented using the `configure` convention. They can all be used in `cmake` with the appropriate format changes. +**`--enable-show-project-config`** (default:OFF) + +When ON, the project configuration is displayed at the end of the CMake Configuration +Step. + + **`--cygwin-use-posix`** (default:OFF) When ON, compile on Cygwin using POSIX API (otherwise it will use MinGW environment). diff --git a/scripts/ShowProjectConfig.cmake b/scripts/ShowProjectConfig.cmake new file mode 100644 index 000000000..dc57876c9 --- /dev/null +++ b/scripts/ShowProjectConfig.cmake @@ -0,0 +1,190 @@ +# +# SRT - Secure, Reliable, Transport Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at http://mozilla.org/MPL/2.0/. +# + +function(ShowProjectConfig) + + set(__ssl_configuration) + if (SSL_FOUND OR SSL_LIBRARIES) + set(__ssl_configuration + " SSL Configuration: + SSL_FOUND=${SSL_FOUND} + SSL_INCLUDE_DIRS=${SSL_INCLUDE_DIRS} + SSL_LIBRARIES=${SSL_LIBRARIES} + SSL_VERSION=${SSL_VERSION}\n") + endif() + + set(static_property_link_libraries) + if (srt_libspec_static) + get_target_property( + static_property_link_libraries + ${TARGET_srt}_static + LINK_LIBRARIES) + endif() + set(shared_property_link_libraries) + if (srt_libspec_shared) + get_target_property( + shared_property_link_libraries + ${TARGET_srt}_shared + LINK_LIBRARIES) + endif() + + # See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13 + set(__more_tc1_config) + if (CMAKE_CROSSCOMPILING) + set(__more_tc1_config + " CMAKE_SYSROOT: ${CMAKE_SYSROOT}\n") + endif() + + # See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13 + set(__more_tc2_config) + if (APPLE) + set(__more_tc2_config + " CMAKE_INSTALL_NAME_TOOL: ${CMAKE_INSTALL_NAME_TOOL} + CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} + CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES} + CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET} + CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}\n") + elseif (ANDROID) + set(__more_tc2_config + " CMAKE_ANDROID_NDK: ${CMAKE_ANDROID_NDK} + CMAKE_ANDROID_STANDALONE_TOOLCHAIN: ${CMAKE_ANDROID_STANDALONE_TOOLCHAIN} + CMAKE_ANDROID_API: ${CMAKE_ANDROID_API} + CMAKE_ANDROID_ARCH_ABI: ${CMAKE_ANDROID_ARCH_ABI} + CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION: ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION} + CMAKE_ANDROID_STL_TYPE: ${CMAKE_ANDROID_STL_TYPE}\n") + endif() + + message(STATUS + "\n" + "========================================================================\n" + "= Project Configuration:\n" + "========================================================================\n" + " SRT Version:\n" + " SRT_VERSION: ${SRT_VERSION}\n" + " SRT_VERSION_BUILD: ${SRT_VERSION_BUILD}\n" + " CMake Configuration:\n" + " CMAKE_VERSION: ${CMAKE_VERSION}\n" + " CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}\n" + " CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" + " Target Configuration:\n" + " CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}\n" + " CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}\n" + " CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}\n" + " CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}\n" + " DARWIN: ${DARWIN}\n" + " LINUX: ${LINUX}\n" + " BSD: ${BSD}\n" + " MICROSOFT: ${MICROSOFT}\n" + " GNU: ${GNU}\n" + " ANDROID: ${ANDROID}\n" + " SUNOS: ${SUNOS}\n" + " POSIX: ${POSIX}\n" + " SYMLINKABLE: ${SYMLINKABLE}\n" + " APPLE: ${APPLE}\n" + " UNIX: ${UNIX}\n" + " WIN32: ${WIN32}\n" + " MINGW: ${MINGW}\n" + " CYGWIN: ${CYGWIN}\n" + " CYGWIN_USE_POSIX: ${CYGWIN_USE_POSIX}\n" + " Toolchain Configuration:\n" + " CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}\n" + " CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}\n" + "${__more_tc1_config}" + " CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}\n" + " CMAKE_C_COMPILER_VERSION: ${CMAKE_C_COMPILER_VERSION}\n" + " CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}\n" + " CMAKE_C_FLAGS: '${CMAKE_C_FLAGS}'\n" + " CMAKE_C_COMPILE_FEATURES: ${CMAKE_C_COMPILE_FEATURES}\n" + " CMAKE_C_STANDARD: ${CMAKE_CXX_STANDARD}\n" + " CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}\n" + " CMAKE_CXX_COMPILER_VERSION: ${CMAKE_CXX_COMPILER_VERSION}\n" + " CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}\n" + " CMAKE_CXX_FLAGS: '${CMAKE_CXX_FLAGS}'\n" + " CMAKE_CXX_COMPILE_FEATURES: ${CMAKE_CXX_COMPILE_FEATURES}\n" + " CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}\n" + " CMAKE_LINKER: ${CMAKE_LINKER}\n" + #" CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}\n" + #" CMAKE_EXE_LINKER_FLAGS_INIT: ${CMAKE_EXE_LINKER_FLAGS_INIT}\n" + #" CMAKE_MODULE_LINKER_FLAGS: ${CMAKE_MODULE_LINKER_FLAGS}\n" + #" CMAKE_MODULE_LINKER_FLAGS_INIT: ${CMAKE_MODULE_LINKER_FLAGS_INIT}\n" + #" CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}\n" + #" CMAKE_SHARED_LINKER_FLAGS_INIT: ${CMAKE_SHARED_LINKER_FLAGS_INIT}\n" + #" CMAKE_STATIC_LINKER_FLAGS: ${CMAKE_STATIC_LINKER_FLAGS}\n" + #" CMAKE_STATIC_LINKER_FLAGS_INIT: ${CMAKE_STATIC_LINKER_FLAGS_INIT}\n" + " CMAKE_NM: ${CMAKE_NM}\n" + " CMAKE_AR: ${CMAKE_AR}\n" + " CMAKE_RANLIB: ${CMAKE_RANLIB}\n" + "${__more_tc2_config}" + " HAVE_COMPILER_GNU_COMPAT: ${HAVE_COMPILER_GNU_COMPAT}\n" + " CMAKE_THREAD_LIBS: ${CMAKE_THREAD_LIBS}\n" + " CMAKE_THREAD_LIBS_INIT: ${CMAKE_THREAD_LIBS_INIT}\n" + " ENABLE_THREAD_CHECK: ${ENABLE_THREAD_CHECK}\n" + " USE_CXX_STD_APP: ${USE_CXX_STD_APP}\n" + " USE_CXX_STD_LIB: ${USE_CXX_STD_LIB}\n" + " STDCXX: ${STDCXX}\n" + " USE_CXX_STD: ${USE_CXX_STD}\n" + " HAVE_CLOCK_GETTIME_IN: ${HAVE_CLOCK_GETTIME_IN}\n" + " HAVE_CLOCK_GETTIME_LIBRT: ${HAVE_CLOCK_GETTIME_LIBRT}\n" + " HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H}\n" + " HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H}\n" + " HAVE_PTHREAD_GETNAME_NP: ${HAVE_PTHREAD_GETNAME_NP}\n" + " HAVE_PTHREAD_SETNAME_NP: ${HAVE_PTHREAD_SETNAME_NP}\n" + " HAVE_LIBATOMIC: ${HAVE_LIBATOMIC}\n" + " HAVE_LIBATOMIC_COMPILES: ${HAVE_LIBATOMIC_COMPILES}\n" + " HAVE_LIBATOMIC_COMPILES_STATIC: ${HAVE_LIBATOMIC_COMPILES_STATIC}\n" + " HAVE_GCCATOMIC_INTRINSICS: ${HAVE_GCCATOMIC_INTRINSICS}\n" + " HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC: ${HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC}\n" + " HAVE_CXX_ATOMIC: ${HAVE_CXX_ATOMIC}\n" + " HAVE_CXX_ATOMIC_STATIC: ${HAVE_CXX_ATOMIC_STATIC}\n" + " Project Configuration:\n" + " ENABLE_DEBUG: ${ENABLE_DEBUG}\n" + " ENABLE_CXX11: ${ENABLE_CXX11}\n" + " ENABLE_APPS: ${ENABLE_APPS}\n" + " ENABLE_EXAMPLES: ${ENABLE_EXAMPLES}\n" + " ENABLE_EXPERIMENTAL_BONDING: ${ENABLE_EXPERIMENTAL_BONDING}\n" + " ENABLE_TESTING: ${ENABLE_TESTING}\n" + " ENABLE_PROFILE: ${ENABLE_PROFILE}\n" + " ENABLE_LOGGING: ${ENABLE_LOGGING}\n" + " ENABLE_HEAVY_LOGGING: ${ENABLE_HEAVY_LOGGING}\n" + " ENABLE_HAICRYPT_LOGGING: ${ENABLE_HAICRYPT_LOGGING}\n" + " ENABLE_SHARED: ${ENABLE_SHARED}\n" + " ENABLE_STATIC: ${ENABLE_STATIC}\n" + " ENABLE_RELATIVE_LIBPATH: ${ENABLE_RELATIVE_LIBPATH}\n" + " ENABLE_GETNAMEINFO: ${ENABLE_GETNAMEINFO}\n" + " ENABLE_UNITTESTS: ${ENABLE_UNITTESTS}\n" + " ENABLE_ENCRYPTION: ${ENABLE_ENCRYPTION}\n" + " ENABLE_CXX_DEPS: ${ENABLE_CXX_DEPS}\n" + " USE_STATIC_LIBSTDCXX: ${USE_STATIC_LIBSTDCXX}\n" + " ENABLE_INET_PTON: ${ENABLE_INET_PTON}\n" + " ENABLE_CODE_COVERAGE: ${ENABLE_CODE_COVERAGE}\n" + " ENABLE_MONOTONIC_CLOCK: ${ENABLE_MONOTONIC_CLOCK}\n" + " ENABLE_STDCXX_SYNC: ${ENABLE_STDCXX_SYNC}\n" + " USE_OPENSSL_PC: ${USE_OPENSSL_PC}\n" + " USE_BUSY_WAITING: ${USE_BUSY_WAITING}\n" + " USE_GNUSTL: ${USE_GNUSTL}\n" + " ENABLE_SOCK_CLOEXEC: ${ENABLE_SOCK_CLOEXEC}\n" + " ENABLE_SHOW_PROJECT_CONFIG: ${ENABLE_SHOW_PROJECT_CONFIG}\n" + " ENABLE_CLANG_TSA: ${ENABLE_CLANG_TSA}\n" + " ATOMIC_USE_SRT_SYNC_MUTEX: ${ATOMIC_USE_SRT_SYNC_MUTEX}\n" + " Constructed Configuration:\n" + " DISABLE_CXX11: ${DISABLE_CXX11}\n" + " HAVE_INET_PTON: ${HAVE_INET_PTON}\n" + " PTHREAD_LIBRARY: ${PTHREAD_LIBRARY}\n" + " USE_ENCLIB: ${USE_ENCLIB}\n" + "${__ssl_configuration}" + " TARGET_srt: ${TARGET_srt}\n" + " srt_libspec_static: ${srt_libspec_static}\n" + " srt_libspec_shared: ${srt_libspec_shared}\n" + " SRT_LIBS_PRIVATE: ${SRT_LIBS_PRIVATE}\n" + " Target Link Libraries:\n" + " Static: ${static_property_link_libraries}\n" + " Shared: ${shared_property_link_libraries}\n" + "========================================================================\n" + ) + +endfunction(ShowProjectConfig) From 3c3824fe28f655c64329a9bb344cb5c59002e7b6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:20:59 +0100 Subject: [PATCH 300/790] [core] Removed unused SRT_DEBUG_TSBPD_DRIFT --- CMakeLists.txt | 2 +- srtcore/buffer.cpp | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e48798713..7d56c1200 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ endforeach() # SRT_ENABLE_ECN 1 /* Early Congestion Notification (for source bitrate control) */ # SRT_DEBUG_TSBPD_OUTJITTER 1 /* Packet Delivery histogram */ -# SRT_DEBUG_TSBPD_DRIFT 1 /* Debug Encoder-Decoder Drift) */ +# SRT_DEBUG_TRACE_DRIFT 1 /* Create a trace log for Encoder-Decoder Clock Drift */ # SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */ # SRT_DEBUG_TLPKTDROP_DROPSEQ 1 # SRT_DEBUG_SNDQ_HIGHRATE 1 diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e861654eb..c43cc4c59 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -816,14 +816,6 @@ void CSndBuffer::increase() * m_iMaxPos: none? (modified on add and ack */ -// XXX Init values moved to in-class. -// const uint32_t CRcvBuffer::TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec) -// const int CRcvBuffer::TSBPD_DRIFT_MAX_VALUE = 5000; // usec -// const int CRcvBuffer::TSBPD_DRIFT_MAX_SAMPLES = 1000; // ACK-ACK packets -#ifdef SRT_DEBUG_TSBPD_DRIFT -// const int CRcvBuffer::TSBPD_DRIFT_PRT_SAMPLES = 200; // ACK-ACK packets -#endif - CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) : m_pUnit(NULL) , m_iSize(bufsize_pkts) @@ -842,11 +834,6 @@ CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) for (int i = 0; i < m_iSize; ++i) m_pUnit[i] = NULL; -#ifdef SRT_DEBUG_TSBPD_DRIFT - memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); - memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); -#endif - setupMutex(m_BytesCountLock, "BytesCount"); } From ec571a034990499df0cae406873084a58dbe8cb1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:23:30 +0100 Subject: [PATCH 301/790] [core] Fixed new RCV buffer in stream mode (reading fractional packets) --- srtcore/buffer_rcv.cpp | 54 ++++++++++++------------------------------ srtcore/buffer_rcv.h | 2 +- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 12e935057..0ef1d75de 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -369,9 +369,10 @@ namespace { /// @brief Writes bytes to file stream. /// @param data pointer to data to write. /// @param len the number of bytes to write + /// @param dst_offset ignored /// @param arg a void pointer to the fstream to write to. /// @return true on success, false on failure - bool writeBytesToFile(char* data, int len, void* arg) + bool writeBytesToFile(char* data, int len, int dst_offset SRT_ATR_UNUSED, void* arg) { fstream* pofs = reinterpret_cast(arg); pofs->write(data, len); @@ -381,11 +382,12 @@ namespace { /// @brief Copies bytes to the destination buffer. /// @param data pointer to data to copy. /// @param len the number of bytes to copy + /// @param dst_offset offset in destination buffer /// @param arg A pointer to the destination buffer /// @return true on success, false on failure - bool copyBytesToBuf(char* data, int len, void* arg) + bool copyBytesToBuf(char* data, int len, int dst_offset, void* arg) { - char* dst = reinterpret_cast(arg); + char* dst = reinterpret_cast(arg) + dst_offset; memcpy(dst, data, len); return true; } @@ -427,7 +429,7 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) const int remain_pktlen = pktlen - m_iNotch; const int unitsize = std::min(remain_pktlen, rs); - if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, arg)) + if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, len - rs, arg)) break; if (rs >= remain_pktlen) @@ -622,45 +624,23 @@ void CRcvBufferNew::updateNonreadPos() if (m_iMaxPosInc == 0) return; - // const PacketBoundary boundary = packet.getMsgBoundary(); - - //// The simplest case is when inserting a sequential PB_SOLO packet. - // if (boundary == PB_SOLO && (m_iFirstNonreadPos + 1) % m_szSize == pos) - //{ - // m_iFirstNonreadPos = pos; - // return; - //} const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; - while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST)) + while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - // bool good = true; - - // look ahead for the whole message - - // We expect to see either of: - // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - // [PB_SOLO] - // but not: - // [PB_FIRST] NULL ... - // [PB_FIRST] FREE/PASSACK/DROPPED... - // If the message didn't look as expected, interrupt this. - - // This begins with a message starting at m_iStartPos - // up to end_pos (excluding) OR until the PB_LAST message is found. - // If any of the units on this way isn't good, this OUTER loop - // will be interrupted. - for (int i = pos; i != end_pos; i = (i + 1) % m_szSize) + if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + break; + + for (int i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { - // good = false; break; } - // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + // Check PB_LAST only in message mode. + if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -672,11 +652,6 @@ void CRcvBufferNew::updateNonreadPos() pos = m_iFirstNonreadPos; } - - // 1. If there is a gap between this packet and m_iLastReadablePos - // then no sense to update m_iLastReadablePos. - - // 2. The simplest case is when this is the first sequential packet } int CRcvBufferNew::findLastMessagePkt() @@ -929,9 +904,10 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t { ss << ":n/a ms"; } + ss << ". "; } - ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + ss << SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; return ss.str(); } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index dfc63d3f6..490d281d9 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -233,7 +233,7 @@ class CRcvBufferNew int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; - typedef bool copy_to_dst_f(char* data, int len, void* arg); + typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); /// Read acknowledged data directly into file. /// @param [in] ofs C++ file stream. From 01ef57a9a9e8c0a1e6b6615bdf7849959a99a482 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:59:57 +0100 Subject: [PATCH 302/790] [tests] Added unit tests for RCV buffer in stream mode --- test/test_buffer.cpp | 116 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 896303d59..2ba636525 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -11,12 +11,13 @@ class CRcvBufferReadMsg : public ::testing::Test { protected: - CRcvBufferReadMsg() + CRcvBufferReadMsg(bool message_api = true) + : m_use_message_api(message_api) { // initialization code here } - ~CRcvBufferReadMsg() + virtual ~CRcvBufferReadMsg() { // cleanup any pending stuff, but no exceptions allowed } @@ -31,7 +32,7 @@ class CRcvBufferReadMsg m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); #if ENABLE_NEW_RCVBUFFER - const bool enable_msg_api = true; + const bool enable_msg_api = m_use_message_api; const bool enable_peer_rexmit = true; m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); #else @@ -161,6 +162,7 @@ class CRcvBufferReadMsg const int m_init_seqno = 1000; int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; + const bool m_use_message_api; const sync::steady_clock::time_point m_tsbpd_base = sync::steady_clock::now(); // now() - HS.timestamp, microseconds const sync::steady_clock::duration m_delay = sync::milliseconds_from(200); @@ -834,4 +836,112 @@ TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } + +class CRcvBufferReadStream + : public CRcvBufferReadMsg +{ +protected: + CRcvBufferReadStream() + : CRcvBufferReadMsg(false) + {} + + virtual ~CRcvBufferReadStream() { } +}; + + +// Add ten packets to the buffer in stream mode, read some of them. +// Try to add packets to occupied positions. +TEST_F(CRcvBufferReadStream, ReadSinglePackets) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0); + } + + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + // Now acknowledge two packets + const int ack_pkts = 2; + ackPackets(2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts); + EXPECT_TRUE(hasAvailablePackets()); + + std::array buff; + for (int i = 0; i < ack_pkts; ++i) + { + const size_t res = m_rcv_buffer->readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } + + // Add packet to the position of oackets already read. + // Can't check the old buffer, as it does not handle a negative offset. + EXPECT_EQ(addPacket(m_init_seqno), -2); + + // Add packet to a non-empty position. + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); + + const int num_pkts_left = num_pkts - ack_pkts; + ackPackets(num_pkts_left); + for (int i = 0; i < num_pkts_left; ++i) + { + const int res = m_rcv_buffer->readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + + +// Add packets to the buffer in stream mode. Read fractional number of packets +// to confirm a partially read packet stays in the buffer and is read properly afterwards. +TEST_F(CRcvBufferReadStream, ReadFractional) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0); + } + + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + array buff; + + const size_t nfull_pkts = 2; + const size_t num_bytes1 = nfull_pkts * m_payload_sz + m_payload_sz / 2; + const int res1 = m_rcv_buffer->readBuffer(buff.data(), num_bytes1); + EXPECT_TRUE(size_t(res1) == num_bytes1); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - nfull_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + const size_t num_bytes2 = m_payload_sz * (num_pkts - nfull_pkts - 1) + m_payload_sz / 2; + + const int res2 = m_rcv_buffer->readBuffer(buff.data() + num_bytes1, buff.size() - num_bytes1); + EXPECT_TRUE(size_t(res2) == num_bytes2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts - 1); + EXPECT_FALSE(hasAvailablePackets()); + ackPackets(num_pkts); // Move the reference ACK position. + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))) << "i = " << i; + } + + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + #endif // ENABEL_NEW_RCVBUFFER From e4a1d2b01919809f8a0efb96788261689202ccd7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Nov 2021 16:42:24 +0100 Subject: [PATCH 303/790] [core] Fixed read-ready epoll event in stream (file) mode. Only the new RCV buffer (PR #1964) is affected. --- srtcore/core.cpp | 80 ++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d0849c6b8..9aa2c5eb2 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6275,8 +6275,8 @@ int srt::CUDT::receiveBuffer(char *data, int len) return 0; } HLOGC(arlog.Debug, - log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") - << " SHUTDOWN. Reporting as BROKEN."); + log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6288,31 +6288,29 @@ int srt::CUDT::receiveBuffer(char *data, int len) { throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } - else + + // Kick TsbPd thread to schedule next wakeup (if running) + if (m_config.iRcvTimeOut < 0) { - /* Kick TsbPd thread to schedule next wakeup (if running) */ - if (m_config.iRcvTimeOut < 0) + THREAD_PAUSED(); + while (stillConnected() && !isRcvBufferReady()) { - THREAD_PAUSED(); - while (stillConnected() && !isRcvBufferReady()) - { - // Do not block forever, check connection status each 1 sec. - rcond.wait_for(seconds_from(1)); - } - THREAD_RESUMED(); + // Do not block forever, check connection status each 1 sec. + rcond.wait_for(seconds_from(1)); } - else + THREAD_RESUMED(); + } + else + { + const steady_clock::time_point exptime = + steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); + THREAD_PAUSED(); + while (stillConnected() && !isRcvBufferReady()) { - const steady_clock::time_point exptime = - steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); - THREAD_PAUSED(); - while (stillConnected() && !isRcvBufferReady()) - { - if (!rcond.wait_until(exptime)) // NOT means "not received a signal" - break; // timeout - } - THREAD_RESUMED(); + if (!rcond.wait_until(exptime)) // NOT means "not received a signal" + break; // timeout } + THREAD_RESUMED(); } } @@ -8066,23 +8064,31 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { UniqueLock rdlock (m_RecvLock); CSync rdcond (m_RecvDataCond, rdlock); - if (m_config.bSynRecving) + +#if ENABLE_NEW_RCVBUFFER + // Locks m_RcvBufferLock, which is unlocked above by InvertedLock un_bufflock. + // Must check read-readiness under m_RecvLock to protect the epoll from concurrent changes in readBuffer() + if (isRcvBufferReady()) +#endif { - // signal a waiting "recv" call if there is any data available - rdcond.signal_locked(rdlock); + if (m_config.bSynRecving) + { + // signal a waiting "recv" call if there is any data available + rdcond.signal_locked(rdlock); + } + // acknowledge any waiting epolls to read + // fix SRT_EPOLL_IN event loss but rcvbuffer still have dataļ¼š + // 1. user call receive/receivemessage(about line number:6482) + // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false + // 3. but if we do not do some lock work here, will cause some sync problems between threads: + // (1) user thread: call receive/receivemessage + // (2) user thread: read data + // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false + // (4) receive thread: receive data and set SRT_EPOLL_IN to true + // (5) user thread: set SRT_EPOLL_IN to false + // 4. so , m_RecvLock must be used here to protect epoll event + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } - // acknowledge any waiting epolls to read - // fix SRT_EPOLL_IN event loss but rcvbuffer still have dataļ¼š - // 1. user call receive/receivemessage(about line number:6482) - // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false - // 3. but if we do not do some lock work here, will cause some sync problems between threads: - // (1) user thread: call receive/receivemessage - // (2) user thread: read data - // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false - // (4) receive thread: receive data and set SRT_EPOLL_IN to true - // (5) user thread: set SRT_EPOLL_IN to false - // 4. so , m_RecvLock must be used here to protect epoll event - uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) From 5f3cd06c49a99b74fc7877f9b8c3c3ff29f7507a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 12 Nov 2021 17:41:12 +0100 Subject: [PATCH 304/790] [core] Fixed std::runtime_error usage (use C++03 version instead of C++11) (#2184) --- srtcore/strerror_defs.cpp | 2 +- srtcore/utilities.h | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index 7393c1279..1b4c72e4e 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -140,7 +140,7 @@ const char* strerror_get_message(size_t major, size_t minor) } const char** array = strerror_array_major[major]; - size_t size = strerror_array_sizes[major]; + const size_t size = strerror_array_sizes[major]; if (minor >= size) { diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 1610505b8..241046ddc 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -44,6 +44,7 @@ written by #include #include #include +#include // -------------- UTILITIES ------------------------ @@ -418,7 +419,8 @@ class FixedArray { public: FixedArray(size_t size) - : m_size(size) + : m_strIndexErr("FixedArray: invalid index") + , m_size(size) , m_entries(new T[size]) { } @@ -432,7 +434,7 @@ class FixedArray const T& operator[](size_t index) const { if (index >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -440,7 +442,7 @@ class FixedArray T& operator[](size_t index) { if (index >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -448,7 +450,7 @@ class FixedArray const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -456,7 +458,7 @@ class FixedArray T& operator[](int index) { if (index < 0 || static_cast(index) >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -468,8 +470,9 @@ class FixedArray FixedArray& operator=(const FixedArray&); private: - size_t m_size; - T* const m_entries; + const char* m_strIndexErr; + size_t m_size; + T* const m_entries; }; } // namespace srt From 33c8e492a0014a8173487f89205aab82067ca4b2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 25 Oct 2021 15:27:54 +0200 Subject: [PATCH 305/790] [core] checkNeedDrop returns the congestion state --- srtcore/core.cpp | 14 +++++++------- srtcore/core.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9aa2c5eb2..1833b25d8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6362,10 +6362,10 @@ int srt::CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -void srt::CUDT::checkNeedDrop(bool& w_bCongestion) +bool srt::CUDT::checkNeedDrop() { if (!m_bPeerTLPktDrop) - return; + return false; if (!m_config.bMessageAPI) { @@ -6390,6 +6390,7 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) (2 * COMM_SYN_INTERVAL_US / 1000); } + bool bCongestion = false; if (threshold_ms && timespan_ms > threshold_ms) { // protect packet retransmission @@ -6447,7 +6448,7 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) } #endif } - w_bCongestion = true; + bCongestion = true; leaveCS(m_RecvAckLock); } else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2)) @@ -6455,8 +6456,9 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) HLOGC(aslog.Debug, log << "cong, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms"); - w_bCongestion = true; + bCongestion = true; } + return bCongestion; } int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) @@ -6473,8 +6475,6 @@ int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64 // which is the only case when the m_parent->m_GroupOf is not NULL. int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { - bool bCongestion = false; - // throw an exception if not connected if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6563,7 +6563,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // checkNeedDrop(...) may lock m_RecvAckLock // to modify m_pSndBuffer and m_pSndLossList - checkNeedDrop((bCongestion)); + const bool bCongestion = checkNeedDrop(); int minlen = 1; // Minimum sender buffer space required for STREAM API if (m_config.bMessageAPI) diff --git a/srtcore/core.h b/srtcore/core.h index 9f957d040..54c3927e2 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -541,7 +541,7 @@ class CUDT void updateIdleLinkFrom(CUDT* source); - void checkNeedDrop(bool& bCongestion); + bool checkNeedDrop(); /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, From b99e41cc2a2b508eb03c0cfd8828ec6121e28375 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 28 Oct 2021 12:33:48 +0200 Subject: [PATCH 306/790] [core] SND buffer: operate either origin or source time, not both. --- srtcore/buffer.cpp | 57 +++++++++++++++++++--------------------------- srtcore/buffer.h | 8 +++---- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index c43cc4c59..7102d09a8 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -193,21 +193,28 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) HLOGC(bslog.Debug, log << "addBuffer: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << size << " buffers for " << len << " bytes"); + // Retrieve current time before locking the mutex to be closer to packet submission event. + const steady_clock::time_point tnow = steady_clock::now(); - // dynamically increase sender buffer + ScopedLock bufferguard(m_BufLock); + // Dynamically increase sender buffer if there is not enough room. while (size + m_iCount >= m_iSize) { HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (size + m_iCount - m_iSize) << " buffers..."); increase(); } - const steady_clock::time_point time = steady_clock::now(); const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0; - HLOGC(bslog.Debug, log << CONID() << "addBuffer: adding " << size << " packets (" << len << " bytes) to send, msgno=" << (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order"); + // Calculate origin time (same for all blocks of the message). + m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow; + // Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it. + // May also be a subject to conversion error, thus the actual value is signalled back. + w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch()); + // The sequence number passed to this function is the sequence number // that the very first packet from the packet series should get here. // If there's more than one packet, this function must increase it by itself @@ -253,33 +260,21 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) // [PB_FIRST] [PB_LAST] - 2 packets per message // [PB_SOLO] - 1 packet per message - s->m_llSourceTime_us = w_srctime; - s->m_tsOriginTime = time; - s->m_tsRexmitTime = time_point(); s->m_iTTL = ttl; - // Rewrite the actual sending time back into w_srctime - // so that the calling facilities can reuse it - if (!w_srctime) - w_srctime = count_microseconds(s->m_tsOriginTime.time_since_epoch()); - - // XXX unchecked condition: s->m_pNext == NULL. + s->m_tsRexmitTime = time_point(); + s->m_tsOriginTime = m_tsLastOriginTime; + // Should never happen, as the call to increase() should ensure enough buffers. SRT_ASSERT(s->m_pNext); s = s->m_pNext; } m_pLastBlock = s; - enterCS(m_BufLock); m_iCount += size; - m_iBytesCount += len; - m_tsLastOriginTime = time; - updateInputRate(time, size, len); - - updAvgBufSize(time); - - leaveCS(m_BufLock); + updateInputRate(m_tsLastOriginTime, size, len); + updAvgBufSize(m_tsLastOriginTime); // MSGNO_SEQ::mask has a form: 00000011111111... // At least it's known that it's from some index inside til the end (to bit 0). @@ -402,16 +397,6 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) return total; } -steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& block) -{ - if (block.m_llSourceTime_us) - { - return steady_clock::time_point() + microseconds_from(block.m_llSourceTime_us); - } - - return block.m_tsOriginTime; -} - int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read @@ -459,7 +444,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; - w_srctime = getSourceTime(*m_pCurrBlock); + w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); @@ -593,7 +578,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // the packet originally (the other overload of this function) must set these // flags. w_packet.m_iMsgNo = p->m_iMsgNoBitset; - w_srctime = getSourceTime(*p); + w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. // Therefore we are setting the rexmit time. @@ -683,11 +668,17 @@ int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) * Also, if there is only one pkt in buffer, the time difference will be 0. * Therefore, always add 1 ms if not empty. */ - w_timespan = 0 < m_iCount ? count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; + w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; return m_iCount; } +CSndBuffer::time_point CSndBuffer::getOldestTime() const +{ + SRT_ASSERT(m_pFirstBlock); + return m_pFirstBlock->m_tsOriginTime; +} + int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) { int dpkts = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 428db748d..9b6259226 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -178,6 +178,8 @@ class CSndBuffer int getAvgBufSize(int& bytes, int& timespan); int getCurrBufSize(int& bytes, int& timespan); + time_point getOldestTime() const; + uint64_t getInRatePeriod() const { return m_InRatePeriod; } /// Retrieve input bitrate in bytes per second @@ -197,9 +199,6 @@ class CSndBuffer void increase(); void setInputRateSmpPeriod(int period); - struct Block; // Defined below - static time_point getSourceTime(const CSndBuffer::Block& block); - private: // Constants static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms @@ -216,9 +215,8 @@ class CSndBuffer int32_t m_iMsgNoBitset; // message number int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // original request time + time_point m_tsOriginTime; // block origin time (either provided from above or equials the time a message was submitted for sending. time_point m_tsRexmitTime; // packet retransmission time - uint64_t m_llSourceTime_us; int m_iTTL; // time to live (milliseconds) Block* m_pNext; // next block From 6ae42c67b85ab09a5c79f4d9173f0c54486b9b32 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 24 Nov 2021 17:49:06 +0800 Subject: [PATCH 307/790] [core] Drop msg by TTL even if hasn't ever been sent (#2068) --- docs/API/API-functions.md | 7 +-- srtcore/buffer.cpp | 107 +++++++++++++++++++++----------------- srtcore/buffer.h | 15 +++--- srtcore/core.cpp | 21 +++++--- 4 files changed, 83 insertions(+), 67 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 58b729fad..e1043b9f4 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1703,11 +1703,8 @@ called function should work. - `msgttl`: [IN]. In **message** and **live mode** only, specifies the TTL for sending messages (in `[ms]`). Not used for receiving messages. If this value is not negative, it defines the maximum time up to which this message should -stay scheduled for sending for the sake of later retransmission. A message -is always sent for the first time, but the UDP packet carrying it may be -(also partially) lost, and if so, lacking packets will be retransmitted. If -the message is not successfully resent before TTL expires, further retransmission -is given up and the message is discarded. +stay scheduled for sending. If TTL has expired, the message sending and further retransmissions are discarded, even +if it has never been sent so far. - `inorder`: [IN]. In **message mode** only, specifies that sent messages should be extracted by the receiver in the order of sending. This can be meaningful if diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 7102d09a8..d07080981 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -397,57 +397,70 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) return total; } -int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc) { - // No data to read - if (m_pCurrBlock == m_pLastBlock) - return 0; + int readlen = 0; + w_seqnoinc = 0; + + while (m_pCurrBlock != m_pLastBlock) + { + // Make the packet REFLECT the data stored in the buffer. + w_packet.m_pcData = m_pCurrBlock->m_pcData; + readlen = m_pCurrBlock->m_iLength; + w_packet.setLength(readlen); + w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + + // XXX This is probably done because the encryption should happen + // just once, and so this sets the encryption flags to both msgno bitset + // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption + // happen at the time when scheduling a new packet to send, but the packet + // must remain in the send buffer until it's ACKed. For the case of rexmit + // the packet will be taken "as is" (that is, already encrypted). + // + // The problem is in the order of things: + // 0. When the application stores the data, some of the flags for PH_MSGNO are set. + // 1. The readData() is called to get the original data sent by the application. + // 2. The data are original and must be encrypted. They WILL BE encrypted, later. + // 3. So far we are in readData() so the encryption flags must be updated NOW because + // later we won't have access to the block's data. + // 4. After exiting from readData(), the packet is being encrypted. It's immediately + // sent, however the data must remain in the sending buffer until they are ACKed. + // 5. In case when rexmission is needed, the second overloaded version of readData + // is being called, and the buffer + PH_MSGNO value is extracted. All interesting + // flags must be present and correct at that time. + // + // The only sensible way to fix this problem is to encrypt the packet not after + // extracting from here, but when the packet is stored into CSndBuffer. The appropriate + // flags for PH_MSGNO will be applied directly there. Then here the value for setting + // PH_MSGNO will be set as is. + + if (kflgs == -1) + { + HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); + readlen = 0; + } + else + { + m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); + } - // Make the packet REFLECT the data stored in the buffer. - w_packet.m_pcData = m_pCurrBlock->m_pcData; - int readlen = m_pCurrBlock->m_iLength; - w_packet.setLength(readlen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; - - // XXX This is probably done because the encryption should happen - // just once, and so this sets the encryption flags to both msgno bitset - // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption - // happen at the time when scheduling a new packet to send, but the packet - // must remain in the send buffer until it's ACKed. For the case of rexmit - // the packet will be taken "as is" (that is, already encrypted). - // - // The problem is in the order of things: - // 0. When the application stores the data, some of the flags for PH_MSGNO are set. - // 1. The readData() is called to get the original data sent by the application. - // 2. The data are original and must be encrypted. They WILL BE encrypted, later. - // 3. So far we are in readData() so the encryption flags must be updated NOW because - // later we won't have access to the block's data. - // 4. After exiting from readData(), the packet is being encrypted. It's immediately - // sent, however the data must remain in the sending buffer until they are ACKed. - // 5. In case when rexmission is needed, the second overloaded version of readData - // is being called, and the buffer + PH_MSGNO value is extracted. All interesting - // flags must be present and correct at that time. - // - // The only sensible way to fix this problem is to encrypt the packet not after - // extracting from here, but when the packet is stored into CSndBuffer. The appropriate - // flags for PH_MSGNO will be applied directly there. Then here the value for setting - // PH_MSGNO will be set as is. + Block* p = m_pCurrBlock; + w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_srctime = m_pCurrBlock->m_tsOriginTime; + m_pCurrBlock = m_pCurrBlock->m_pNext; - if (kflgs == -1) - { - HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); - readlen = 0; - } - else - { - m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); - } - - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; - w_srctime = m_pCurrBlock->m_tsOriginTime; - m_pCurrBlock = m_pCurrBlock->m_pNext; + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL)) + { + LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL); + // Skip this packet due to TTL expiry. + readlen = 0; + ++w_seqnoinc; + continue; + } - HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + break; + } return readlen; } diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9b6259226..6478e30b9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -142,17 +142,18 @@ class CSndBuffer int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] w_packet data packet buffer to fill. - /// @param [out] w_origintime origin time stamp of the message. - /// @param [in] kflags Odd|Even crypto key flag. + /// @param [out] packet the packet to read. + /// @param [out] origintime origin time stamp of the message + /// @param [in] kflags Odd|Even crypto key flag + /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. /// @return Actual length of data read. - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); /// Find data position to pack a DATA packet for a retransmission. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] w_packet data packet buffer to fill. - /// @param [out] w_origintime origin time stamp of the message - /// @param [out] w_msglen length of the message + /// @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). int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1833b25d8..b140c6422 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9219,17 +9219,15 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi SRT_ASSERT(msglen >= 1); seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); - HLOGC(qrlog.Debug, log << "IPE: loss-reported packets not found in SndBuf - requesting DROP: " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" - << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); + HLOGC(qrlog.Debug, + log << "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)); - // only one msg drop request is necessary - m_pSndLossList->removeUpTo(seqpair[1]); - // skip all dropped packets + m_pSndLossList->removeUpTo(seqpair[1]); m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); - continue; } else if (payload == 0) @@ -9327,7 +9325,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. kflg = m_pCryptoControl->getSndCryptoFlags(); - payload = m_pSndBuffer->readData((w_packet), (origintime), kflg); + int pktskipseqno = 0; + payload = m_pSndBuffer->readData((w_packet), (origintime), kflg, (pktskipseqno)); + if (pktskipseqno) + { + // Some packets were skipped due to TTL expiry. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); + } + if (payload) { // A CHANGE. The sequence number is currently added to the packet From e5a11795326249c65a761b413c90fd2871bdaab1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 1 Dec 2021 03:43:10 -0600 Subject: [PATCH 308/790] [core] Fix UDP RCVBUF and SNDBUF on Solaris (#2162). Check system maximum is not exceeded. --- srtcore/channel.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 85c39ba95..8451e5b66 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -265,7 +265,81 @@ void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) void srt::CChannel::setUDPSockOpt() { -#if defined(BSD) || TARGET_OS_MAC +#if defined(SUNOS) + { + socklen_t optSize; + // Retrieve starting SND/RCV Buffer sizes. + int startRCVBUF = 0; + optSize = sizeof(startRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)&startRCVBUF, &optSize)) + { + startRCVBUF = -1; + } + int startSNDBUF = 0; + optSize = sizeof(startSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (void*)&startSNDBUF, &optSize)) + { + startSNDBUF = -1; + } + + // SunOS will fail setsockopt() if the requested buffer size exceeds system + // maximum value. + // However, do not reduce the buffer size. + const int maxsize = 64000; + if (0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) + { + int currentRCVBUF = 0; + optSize = sizeof(currentRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)¤tRCVBUF, &optSize)) + { + currentRCVBUF = -1; + } + if (maxsize > currentRCVBUF) + { + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&maxsize, sizeof maxsize); + } + } + if (0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) + { + int currentSNDBUF = 0; + optSize = sizeof(currentSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)¤tSNDBUF, &optSize)) + { + currentSNDBUF = -1; + } + if (maxsize > currentSNDBUF) + { + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&maxsize, sizeof maxsize); + } + } + + // Retrieve ending SND/RCV Buffer sizes. + int endRCVBUF = 0; + optSize = sizeof(endRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)&endRCVBUF, &optSize)) + { + endRCVBUF = -1; + } + int endSNDBUF = 0; + optSize = sizeof(endSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (void*)&endSNDBUF, &optSize)) + { + endSNDBUF = -1; + } + LOGC(kmlog.Debug, + log << "SO_RCVBUF:" + << " startRCVBUF=" << startRCVBUF << " m_mcfg.iUDPRcvBufSize=" << m_mcfg.iUDPRcvBufSize + << " endRCVBUF=" << endRCVBUF); + LOGC(kmlog.Debug, + log << "SO_SNDBUF:" + << " startSNDBUF=" << startSNDBUF << " m_mcfg.iUDPSndBufSize=" << m_mcfg.iUDPSndBufSize + << " endSNDBUF=" << endSNDBUF); + } +#elif defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value int maxsize = 64000; if (0 != ::setsockopt( From 8b68157d57a4df9f95ba02d37c3fce64c0d480c5 Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Wed, 1 Dec 2021 21:34:35 +0800 Subject: [PATCH 309/790] [core] fix CRcvLossList::m_iTail not reset to -1 --- srtcore/list.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index e295fb8c8..9459e99bd 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -583,6 +583,8 @@ bool CRcvLossList::remove(int32_t seqno) m_iHead = m_caSeq[m_iHead].inext; if (-1 != m_iHead) m_caSeq[m_iHead].iprior = -1; + else + m_iTail = -1; } else { From 244d2f41d1154b3790672a10ff1178edd68979ad Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 2 Dec 2021 15:48:21 +0800 Subject: [PATCH 310/790] [core] Fix deadlock introduced by CUDTGroup::setOpt() --- srtcore/group.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c27ae2cbe..a06be0fb8 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -444,10 +444,19 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) HLOGC(gmlog.Debug, log << "... SPREADING to existing sockets."); // This means that there are sockets already, so apply // this option on them. - ScopedLock gg(m_GroupLock); - for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + std::vector ps_vec; + { + // Do copy to avoid deadlock. CUDT::setOpt() cannot be called directly inside this loop, because + // CUDT::setOpt() will lock m_ConnectionLock, which should be locked before m_GroupLock. + ScopedLock gg(m_GroupLock); + for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + ps_vec.push_back(gi->ps); + } + } + for (std::vector::iterator it = ps_vec.begin(); it != ps_vec.end(); ++it) { - gi->ps->core().setOpt(optName, optval, optlen); + (*it)->core().setOpt(optName, optval, optlen); } } From c9a8db75e865925f156e0d1c3eeea02ec43e1208 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Fri, 3 Dec 2021 21:35:08 +0800 Subject: [PATCH 311/790] [core] Fix consistency of packet seqno in CRcvLossList (#2195) Make sure data to be inserted into CRcvLossList is monotonic, i.e. don't insert sequence numbers less (earlier) than the latest one. --- srtcore/list.cpp | 24 +++++++++++++++++++++++- srtcore/list.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 9459e99bd..ec63c8755 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -487,6 +487,7 @@ CRcvLossList::CRcvLossList(int size) , m_iTail(-1) , m_iLength(0) , m_iSize(size) + , m_iLargestSeq(SRT_SEQNO_NONE) { m_caSeq = new Seq[m_iSize]; @@ -506,7 +507,25 @@ CRcvLossList::~CRcvLossList() void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) { // Data to be inserted must be larger than all those in the list - // guaranteed by the UDT receiver + if (m_iLargestSeq != SRT_SEQNO_NONE && CSeqNo::seqcmp(seqno1, m_iLargestSeq) <= 0) + { + if (CSeqNo::seqcmp(seqno2, m_iLargestSeq) > 0) + { + LOGC(qrlog.Warn, + log << "RCV-LOSS/insert: seqno1=" << seqno1 << " too small, adjust to " + << CSeqNo::incseq(m_iLargestSeq)); + seqno1 = CSeqNo::incseq(m_iLargestSeq); + } + else + { + LOGC(qrlog.Warn, + log << "RCV-LOSS/insert: (" << seqno1 << "," << seqno2 + << ") to be inserted is too small: m_iLargestSeq=" << m_iLargestSeq << ", m_iLength=" << m_iLength + << ", m_iHead=" << m_iHead << ", m_iTail=" << m_iTail << " -- REJECTING"); + return; + } + } + m_iLargestSeq = seqno2; if (0 == m_iLength) { @@ -561,6 +580,9 @@ void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) bool CRcvLossList::remove(int32_t seqno) { + if (m_iLargestSeq == SRT_SEQNO_NONE || CSeqNo::seqcmp(seqno, m_iLargestSeq) > 0) + m_iLargestSeq = seqno; + if (0 == m_iLength) return false; diff --git a/srtcore/list.h b/srtcore/list.h index 19b300f30..d79349aa7 100644 --- a/srtcore/list.h +++ b/srtcore/list.h @@ -185,6 +185,7 @@ class CRcvLossList int m_iTail; // last node in the list; int m_iLength; // loss length int m_iSize; // size of the static array + int m_iLargestSeq; // largest seq ever seen private: CRcvLossList(const CRcvLossList&); From 40b51294b94b0d2cc130bc5718d9ae5b8f2a6ab0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Dec 2021 10:51:47 +0100 Subject: [PATCH 312/790] [apps] Added support for adapter parameter in caller mode (#2202) --- apps/transmitmedia.cpp | 4 ++-- testing/testmedia.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index c61c703b8..da67bdaee 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -377,9 +377,9 @@ void SrtCommon::OpenClient(string host, int port) { PrepareClient(); - if ( m_outgoing_port ) + if (m_outgoing_port || m_adapter != "") { - SetupAdapter("", m_outgoing_port); + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(host, port); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 932406888..a5006c48d 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -881,9 +881,9 @@ void SrtCommon::OpenClient(string host, int port) { PrepareClient(); - if (m_outgoing_port) + if (m_outgoing_port || m_adapter != "") { - SetupAdapter("", m_outgoing_port); + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(host, port); @@ -2580,10 +2580,10 @@ void SrtModel::Establish(std::string& w_name) Verb() << "NO STREAM ID for SRT connection"; } - if (m_outgoing_port) + if (m_outgoing_port || m_adapter != "") { - Verb() << "Setting outgoing port: " << m_outgoing_port; - SetupAdapter("", m_outgoing_port); + Verb() << "Setting outgoing port: " << m_outgoing_port << " adapter:" << m_adapter; + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(m_host, m_port); From 1d808c1501b426ac95cced21be8c12f78af31f4e Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Mon, 6 Dec 2021 20:20:57 +0800 Subject: [PATCH 313/790] [core] fix recv_WaitForReadReady() return empty --- srtcore/group.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index a06be0fb8..c9c01378a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2041,6 +2041,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // This can only happen when 0 is passed as timeout and none is ready. // And 0 is passed only in non-blocking mode. So this is none ready in // non-blocking mode. + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } @@ -2329,6 +2330,14 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // m_GlobControlLock lifted, m_GroupLock still locked. // Now we can safely do this scoped way. + if (!m_bSynRecving && ready_sockets.empty()) + { + HLOGC(grlog.Debug, + log << "group/rcv $" << m_GroupID << ": Not available AT THIS TIME, NOT READ-READY now."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + // Ok, now we need to have some extra qualifications: // 1. If a socket has no registry yet, we read anyway, just // to notify the current position. We read ONLY ONE PACKET this time, From 545700fd81d3c121ec807ea4c6310dad3eb95eea Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Dec 2021 15:30:40 +0100 Subject: [PATCH 314/790] [docs] Added and updated description of useful test applications (#2191) --- docs/README.md | 14 ++-- docs/apps/srt-file-transmit.md | 55 +++++++++++++ ...srt-multiplex.md => srt-test-multiplex.md} | 81 ++++++++++--------- docs/apps/srt-test-relay.md | 34 ++++++++ docs/apps/srt-tunnel.md | 10 +-- 5 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 docs/apps/srt-file-transmit.md rename docs/apps/{srt-multiplex.md => srt-test-multiplex.md} (55%) create mode 100644 docs/apps/srt-test-relay.md diff --git a/docs/README.md b/docs/README.md index 55a632bcc..9f07f3e06 100644 --- a/docs/README.md +++ b/docs/README.md @@ -46,12 +46,14 @@ ## Sample Applications -| Document Title | Folder | File Name | Description | -| :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | -| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | -| [Using the
`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | -| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | -| | | | | +| Document Title | Folder | File Name | Description | +| :--------------------------------------------------------------------- | :-------------------- | :-------------------------------------------------- | :------------------------------------------------------------ | +| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the
`srt-file-transmit` App](apps/srt-file-transmit.md) | [apps](apps/) | [srt-file-transmit.md](apps/srt-file-transmit.md) | A sample application to transmit a file over SRT | +| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| [Using the
`srt-test-multiplex` App](apps/srt-test-multiplex.md) | [apps](apps/) | [srt-test-multiplex.md](apps/srt-test-multiplex.md) | Testing application that allows to send multiple streams over one UDP link. | +| [Using the
`srt-test-relay` App](apps/srt-test-relay.md) | [apps](apps/) | [srt-test-relay.md](apps/srt-test-relay.md) | Testing application for bidirectional stream sending over one connection. | +| | | | | ## Miscellaneous diff --git a/docs/apps/srt-file-transmit.md b/docs/apps/srt-file-transmit.md new file mode 100644 index 000000000..2bf3b1115 --- /dev/null +++ b/docs/apps/srt-file-transmit.md @@ -0,0 +1,55 @@ +# srt-file-transmit + +The `srt-file-transmit` tool is a tool for transmitting files over SRT. + +You need: + + - a file to transmit + - a destination to store it into + - this application run on both sides, one sending, one receiving + +## Introduction + +The `srt-file-transmit` application will transmit your file over the SRT connection, +the application on the other side will receive it and store to the desired location. +Both caller-listener and rendezvous arrangement of the connection are possible +and in whatever direction. + +The `streamid` socket option will be used to pass the filename to the other side +so that it is either written with this name or matched with the filename internally. + +The application the will be sending a file should use the file path as source and +SRT URI as a destination, and vice versa for receiving. + +## Caller mode + +If you use sender in caller mode, then the caller SRT URI destination should be +specified, and the "root name" from the file path will be set to `streamid`. +This will allow the listener to use it when writing. + +If a receiver is used as a caller, then the destination filepath's rootname +will be also passed as `streamid` so that the listener receiver can pick up the +file by name. + +In caller mode you must specify the full filename path of the received or sent +file. + +## Listener mode + +If you use sender in listener mode, then you start it with specifying either the +full filename path, or only the directory where the file is located; in the latter +case the filename will be tried from the `streamid` option extracted from the +connected socket (as set by the other side's caller). If the full filename was +specified, it must match the rootname extraced from this option, or otherwise +transmission will not be done. + +If you use receiver in listener mode, then you start it with specifying either +the full filename path, or just the directory. In the latter case the root name +will be extracted from `streamid` socket option, and this one will be transmitted. + +## Usage + +``` +srt-file-transmit [options] +``` + diff --git a/docs/apps/srt-multiplex.md b/docs/apps/srt-test-multiplex.md similarity index 55% rename from docs/apps/srt-multiplex.md rename to docs/apps/srt-test-multiplex.md index 80dbd0411..8e855a15b 100644 --- a/docs/apps/srt-multiplex.md +++ b/docs/apps/srt-test-multiplex.md @@ -1,26 +1,27 @@ -## srt-multiplex +# srt-test-multiplex -**srt-multiplex** (formerly called "SIPLEX") is a sample program that can send -multiple streams in one direction. This tool demonstrates two SRT features: +**srt-test-multiplex** (formerly called "SIPLEX") is a sample program that can +send multiple streams in one direction. This tool demonstrates two SRT features: - the ability to use a single UDP link (a source and destination pair specified by IP address and port) for multiple SRT connections - the use of the `streamid` socket option to identify multiplexed resources -NOTE: due to changes in the common code that can't be properly handled in the -current form of srt-multiplex, this application is temporarily blocked. Instead -the `srt-test-multiplex` application was added with the same functionality, -although it's recommended for testing purposes only. +NOTE: To make this application compiled, you need the `-DENABLE_TESTING=1` +cmake option. -#### Usage +Note also that this application is intended for demonstration only. It can +simply exit with error message in case of wrong usage or broken connection. -`srt-multiplex -i [id] [id]...` - - Multiplexes data from one or more input URIs to transmit as an SRT stream. - The application reads multiple streams (INPUT URIs), each using a separate SRT - connection. All of the traffic from these connections is sent through the - same UDP link. +## Usage -`srt-multiplex -o [id] [id]...` +`srt-test-multiplex -i [id1] [id2]...` + + - Reads all given input streams and sends them each one over a separate + SRT connection, all using the same remote address and source port (hence + using the same UDP link). + +`srt-test-multiplex -o [id] [id]...` - Transmits data from a multiplexed SRT stream to the specified output URI(s). @@ -29,41 +30,41 @@ options. When `-i` is specified, the URIs provided are used as input, and will be output over the `` socket. The reverse is true for any output URIs specified by `-o`. -Separate connections will be created for every specified URI, although all will -be using the same UDP link. When SRT is in caller mode, the SRT socket created -for transmitting data for a given URI will be set to the `streamid` socket option -from this URI's `id` parameter. When SRT is in listener mode, the `streamid` -option will already be set on the accepted socket, and will be matched with a -URI that has the same value in its `id` parameter. - -This `streamid` is the SRT socket option (`SRTO_STREAMID` in the API). The idea -is that it can be set on a socket used for connecting. When a listener is -getting an accepted socket for that connection, the `streamid` socket option -can be read from it, with the result that it will be the same as was set on -the caller side. - -So, in caller mode, for every stream media URI (input or output) there will be -a separate SRT socket created. This socket will have its `socketid` option -set to the value that is given by user as the `id` parameter attached to a -particular URI. In listener mode this happens in the opposite direction ā€” the -value of the `streamid` option is extracted from the accepted socket, and then -matched against all ids specified with the stream media URIs: +If SRT-URI is caller mode, then for every declared input or output medium a +separate connection will be made, each one using the same source port (if +specified, the same will be used for all connections, otherwise the first one +will be automatically selected and then reused for all next ones) and with the +`streamid` socket option set to the value extracted from the medium's +`id` specified parameter: ``` URI1?id=a --> s1(streamid=a).connect(remhost:2000) URI2?id=b --> s2(streamid=b).connect(remhost:2000) URI3?id=c --> s3(streamid=c).connect(remhost:2000) ``` -And on the listener side: + +If SRT-URI is listener mode, then it will extract the value from `streamid` +socket option and every accepted connection will be matched against the `id` +parameter of the specified input or output medium. ``` (remhost:2000) -> accept --> s(SRT socket) --> in URI array find such that uri.id == s.streamid ``` -#### Examples +Note that the rendezvous mode is not supported because you cannot make +multiple connections over the same UDP link in rendezvous mode. + +This `streamid` is the SRT socket option (`SRTO_STREAMID` in the API). The idea +is that it can be set on a socket used for connecting. When a listener is +getting an accepted socket for that connection, the `streamid` socket option +can be read from it, with the result that it will be the same as was set on +the caller side. + + +## Examples - **Sender:** - - `srt-multiplex srt://remhost:2000 -i udp://:5000?id=low udp://:6000?id=high` + - `srt-test-multiplex srt://remhost:2000 -i udp://:5000?id=low udp://:6000?id=high` - **Receiver:** - - `srt-multiplex srt://:2000 -o output-high.ts?id=high output-low.ts?id=low` + - `srt-test-multiplex srt://:2000 -o output-high.ts?id=high output-low.ts?id=low` In this example a Sender is created which will connect to `remhost` port 2000 using multiple SRT sockets, all of which will be using the same outgoing port. @@ -72,7 +73,7 @@ sockets will reuse that port. Alternatively you can enforce the outgoing port using the `port` parameter with the ``. - **Sender:** - - `srt-multiplex srt://remhost:2000?port=5555 ...` + - `srt-test-multiplex srt://remhost:2000?port=5555 ...` A separate connection is made for every input resource. An appropriate resource ID will be set to each socket assigned to that resource according to the `id` @@ -90,9 +91,9 @@ port=5555 -| --------- ( multiplexed UDP stream ) ---------- |- port=2000 +-- --+ ``` When a socket is accepted on the listener side (the Receiver in this example), -srt-multiplex will search for the resource ID among the registered resources +srt-test-multiplex will search for the resource ID among the registered resources (input/output URIs) and set an ID that matches the one on the caller side. If the resource is not found, the connection is closed immediately. -The srt-multiplex program works the same way for connections initiated by a +The srt-test-multiplex program works the same way for connections initiated by a caller or a listener. diff --git a/docs/apps/srt-test-relay.md b/docs/apps/srt-test-relay.md new file mode 100644 index 000000000..85789b6db --- /dev/null +++ b/docs/apps/srt-test-relay.md @@ -0,0 +1,34 @@ +# srt-test-relay + +**srt-test-relay** is a sample program that can utilize SRT connection for +bidirectional traffic. Hence beside the SRT connection you can specify both +input and output media to and from which the traffic will be flipped. + +Effectively the specified input will be sent through the SRT connection as +output, and the input read from the SRT connection will be redirected to +the given output media. + +NOTE: To make this application compiled, you need the `-DENABLE_TESTING=1` +cmake option. + +Note also that this application is intended for demonstration only. It can +simply exit with error message in case of wrong usage or broken connection. + +## Usage + +`srt-test-relay -i -o ` + +Establish a connection, send to it the stream received from INPUT URI and +write the data read from the SRT connection to OUTPUT URI. + +`srt-test-relay -e -o ` + +Establish a connection, read the data from the SRT connection, and write +them back over the SRT connection, and additionally write them as well +to OUTPUT URI and OUTPUT URI2. + +Note that you can also control the single portion of data to be sent +at once by one sending call by `-c` option and you can use both live +and file mode for the connection (the latter should be simply enforced +by `transtype` socket option). + diff --git a/docs/apps/srt-tunnel.md b/docs/apps/srt-tunnel.md index b38c80038..c664173bf 100644 --- a/docs/apps/srt-tunnel.md +++ b/docs/apps/srt-tunnel.md @@ -1,8 +1,7 @@ -SRT Tunnel -========== +# srt-tunnel -Purpose -------- + +## Purpose SRT Tunnel is a typical tunnelling application, that is, it simply passes the transmission from a given endpoint to another endpoint in both directions. @@ -22,8 +21,7 @@ longer distance, leaving TCP close to the caller and listener locations: --> TCP> --> ``` -Usage ------ +## Usage The `srt-tunnel` command line accepts two argument, beside the options: * Listener: the URI at which this tunnel should await connections From c8cb38fe28e4202dc32e98b573acb56f49e54bf6 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 8 Dec 2021 19:08:17 +0800 Subject: [PATCH 315/790] [core] Fix m_GroupOf->updateReadState() in message mode (#2204) Fix ackDataUpTo(..) with the old RCV buffer. Co-authored-by: hondaxiao Co-authored-by: Maxim Sharabayko --- srtcore/buffer_rcv.h | 6 ++++-- srtcore/core.cpp | 16 +++++----------- srtcore/core.h | 6 ++++++ srtcore/group.h | 5 +++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 490d281d9..589cf2bc0 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -163,8 +163,10 @@ class CRcvBufferNew /// IF skipseqno == -1, no missing packet but 1st not ready to play. PacketInfo getFirstValidPacketInfo() const; - /// Get information on the packets available to be read - /// @returns a pair of sequence numbers + /// Get information on packets available to be read. + /// @returns a pair of sequence numbers (first available; first unavailable). + /// + /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; size_t countReadable() const; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b140c6422..01c234c32 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7734,7 +7734,7 @@ void srt::CUDT::releaseSynch() // [[using locked(m_RcvBufferLock)]]; int32_t srt::CUDT::ackDataUpTo(int32_t ack) { - const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); + const int acksize SRT_ATR_UNUSED = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << m_iRcvLastSkipAck << " -> %" << ack << " (" << acksize << " packets)"); @@ -7750,21 +7750,15 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first << " - %" << range.second); } - return acksize; + return CSeqNo::decseq(range.second); #else // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. if (acksize > 0) { - const int distance = m_pRcvBuffer->ackData(acksize); - return CSeqNo::decseq(ack, distance); + m_pRcvBuffer->ackData(acksize); } - - // If nothing was confirmed, then use the current buffer span - const int distance = m_pRcvBuffer->getRcvDataSize(); - if (distance > 0) - return CSeqNo::decseq(ack, distance); return ack; #endif } @@ -8006,7 +8000,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t first_seq SRT_ATR_UNUSED = ackDataUpTo(ack); + const int32_t group_read_seq SRT_ATR_UNUSED = ackDataUpTo(ack); InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8101,7 +8095,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // The current "APP reader" needs to simply decide as to whether // the next CUDTGroup::recv() call should return with no blocking or not. // When the group is read-ready, it should update its pollers as it sees fit. - m_parent->m_GroupOf->updateReadState(m_SocketID, first_seq); + m_parent->m_GroupOf->updateReadState(m_SocketID, group_read_seq); } } #endif diff --git a/srtcore/core.h b/srtcore/core.h index 54c3927e2..71aef9f0a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1043,7 +1043,13 @@ class CUDT int processConnectRequest(const sockaddr_any& addr, CPacket& packet); 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); + + /// @brief Acknowledge reading position up to the @p seq. + /// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq. + /// @param seq first unacknowledged packet sequence number. + /// @return int32_t ackDataUpTo(int32_t seq); + void handleKeepalive(const char* data, size_t lenghth); /// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer. diff --git a/srtcore/group.h b/srtcore/group.h index 9927a2f6d..c61835e1b 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -341,7 +341,12 @@ class CUDTGroup void addEPoll(int eid); void removeEPollEvents(const int eid); void removeEPollID(const int eid); + + /// @brief Update read-ready state. + /// @param sock member socket ID (unused) + /// @param sequence the latest packet sequence number available for reading. void updateReadState(SRTSOCKET sock, int32_t sequence); + void updateWriteState(); void updateFailedLink(); void activateUpdateEvent(bool still_have_items); From e0aaa44331d7396369531bc65e1b12d70888a480 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 8 Dec 2021 11:04:54 +0100 Subject: [PATCH 316/790] [tests] Minor improvement of the TestSocketOptions StreamIDLenListener test case. --- test/test_socket_options.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index d010de510..654b45164 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -110,8 +110,6 @@ class TestSocketOptions } protected: - // put in any custom data members that you need - sockaddr_in m_sa; SRTSOCKET m_caller_sock = SRT_INVALID_SOCK; SRTSOCKET m_listen_sock = SRT_INVALID_SOCK; @@ -982,6 +980,8 @@ TEST_F(TestSocketOptions, StreamIDFull) ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); } +// Check that StreamID assigned to a listener socket is not inherited by accepted sockets, +// and is not derived by a caller socket. TEST_F(TestSocketOptions, StreamIDLenListener) { string stream_id_13 = "something1234"; @@ -991,18 +991,19 @@ TEST_F(TestSocketOptions, StreamIDLenListener) char buffer[648]; int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(string(buffer), stream_id_13); + EXPECT_EQ(size_t(buffer_len), stream_id_13.size()); StartListener(); const SRTSOCKET accepted_sock = EstablishConnection(); - // Check accepted socket inherits values + // Check accepted and caller sockets do not inherit StreamID. for (SRTSOCKET sock : { m_caller_sock, accepted_sock }) { - for (size_t i = 0; i < sizeof buffer; ++i) - buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); + fill_n(buffer, buffer_len, 'a'); EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "LISTENER"); + EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "CALLER"); } ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); From ae787bf23a79384638370c02cab60f7ae00c13f9 Mon Sep 17 00:00:00 2001 From: hondaxiao Date: Thu, 9 Dec 2021 21:28:36 +0800 Subject: [PATCH 317/790] [core] Fix rtt estimate in bidirectional mode --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 01c234c32..7e8839540 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8457,8 +8457,8 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - crttvar = avg_iir<4>(crttvar, abs(crtt - crtt)); - crtt = avg_iir<8>(crtt, crtt); + crttvar = avg_iir<4>(crttvar, abs(crtt - rtt)); + crtt = avg_iir<8>(crtt, rtt); } m_iSRTT = crtt; m_iRTTVar = crttvar; From 26678fe29b9204b1a353df986052c04b00967015 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Fri, 10 Dec 2021 17:19:59 +0100 Subject: [PATCH 318/790] [core] Fixed the issue with RTT in case of bidirectional transmission introduced when adding atomic types --- srtcore/core.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7e8839540..fc86d8a98 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8452,16 +8452,14 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // TODO: The case of bidirectional transmission requires further // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - - int crtt = m_iSRTT.load(), crttvar = m_iRTTVar.load(); - - if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - crttvar = avg_iir<4>(crttvar, abs(crtt - rtt)); - crtt = avg_iir<8>(crtt, rtt); + int iSRTT = m_iSRTT.load(), iRTTVar = m_iRTTVar.load(); + iRTTVar = avg_iir<4>(iRTTVar, abs(rtt - iSRTT)); + iSRTT = avg_iir<8>(iSRTT, rtt); + m_iSRTT = iSRTT; + m_iRTTVar = iRTTVar; } - m_iSRTT = crtt; - m_iRTTVar = crttvar; } else // Transmission is unidirectional. { From cf34d69f0b595c76e8bc9ef9a12f1ba583f1a603 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 13 Dec 2021 12:04:54 +0100 Subject: [PATCH 319/790] [docs] Updated options description for srt-live-transmit (#2209) --- docs/apps/srt-live-transmit.md | 110 +++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 287a47649..258a5bb02 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -107,6 +107,16 @@ The specification and meaning of the fields in the URI depend on the mode. The **PORT** part is always mandatory and it designates either the port number for the target host or the port number to be bound to read from. +The following options are available through URI parameters: + +- **iptos**: sets the `IP_TOS` socket option +- **ttl**: sets the `IP_TTL` or `IP_MULTICAST_TTL` option, depending on mode +- **mcloop**: sets the `IP_MULTICAST_LOOP` option (multicast mode only) +- **rcvbuf**: sets the `SO_RCVBUF` socket option +- **sndbuf**: sets the `SO_SNDBUF` socket option +- **adapter**: sets the local binding address +- **source**: uses `IP_ADD_SOURCE_MEMBERSHIP`, see below for details + For sending to unicast: ```yaml @@ -173,7 +183,7 @@ options that can be set through the parameters. SRT can be connected using one of three connection modes: - **caller**: the "agent" (this application) sends the connection request to - the peer, which must be **listener**, and this way it initiates the +the peer, which must be **listener**, and this way it initiates the connection. - **listener**: the "agent" waits to be contacted by any peer **caller**. @@ -182,50 +192,73 @@ does not use this ability; after the first connection, it no longer accepts new connections. - **rendezvous**: A one-to-one only connection where both parties are - equivalent and both connect to one another simultaneously. Whoever happened -to start first (or succeeded to punch through the firewall) is meant to have +equivalent and both attempt to initiate a connection simultaneously. Whichever party happens +to start first (or succeeds in punching through the firewall first) is considered to have initiated the connection. This mode can be specified explicitly using the **mode** parameter. When it's -not specified, then it is "deduced" the following way: +not specified, then it is derived based on the *host* part in the URI and +the presence of the **adapter** parameter: -- `srt://:1234` - the *port* is specified (1234), but *host* is empty. This assumes **listener** mode. -- `srt://remote.host.com:1234` - both *host* and *port* are specified. This assumes **caller** mode. +* Listener mode: if you leave the *host* part empty (**adapter** may be specified): + - `srt://:1234` +* Caller mode: if you specify *host* part, but not **adapter** parameter: + - `srt://remote.host.com:1234` +* Rendezvous mode: if you specify *host* AND **adapter** parameter: + - `srt://remote.host.com:1234&adapter=my.remote.addr` -When the `mode` parameter is specified explicitly, then the interpretation of the `host` part is the following: +Sometimes the required parameter specification results in a different mode +than desired; in this case you should specify the mode explicitly. -- For caller, it's always the destination host address. If this is empty, it is -resolved to 0.0.0.0, which usually should mean connecting to the local host +The interpretation of the *host* and *port* parts is the following: -- For listener, it defines the IP address of the local device on which the -socket should listen, e.g.: +- In **LISTENER** mode: + - *host* part: the local IP address to bind (default: 0.0.0.0 - "all devices") + - *port* part: the local port to bind (mandatory) + - **adapter** parameter: alternative for *host* part, e.g.: ```yaml srt://10.10.10.100:5001?mode=listener ``` -An alternative method to specify this IP address is the `adapter` parameter: +or ```yaml srt://:5001?adapter=10.10.10.100 ``` -The **rendezvous** mode is not deduced and it has to be specified -explicitly. Note also special cases of the **host** and **port** parts -specified in the URI: - -- **CALLER**: the *host* and *port* parts are mandatory and specify the remote host and port to be contacted. - - The **port** parameter can be used to enforce the local outgoing port (**not to be confused** with remote port!). - - The **adapter** parameter is not used. -- **LISTENER**: the *port* part is mandatory and it specifies the local listening port. - - The **adapter** parameter can be used to specify the adapter. - - The *host* part, if specified, can be also used to set the adapter - although in this case **mode=listener** must be set explicitly. - - The **port** parameter is not used. -- **RENDEZVOUS**: the *host* and *port* parts are mandatory. - - The *host* part specifies the remote host to contact. - - The *port* part specifies **both local and remote port**. Note that the local port is this way both listening port and outgoing port. - - The **adapter** parameter can be used to specify the adapter. - - The **port** parameter can be used to specify the local port to bind to. +- In **CALLER** mode: + - *host* part: remote IP address to connect to (mandatory) + - *port* part: remote port to connect to (mandatory) + - **port** parameter: the local port to bind (default: 0 - "system autoselection") + - **adapter** parameter: the local IP address to bind (default: 0.0.0.0 - "system selected device") + +```yaml +srt://remote.host.com:5001 +``` + +```yaml +srt://remote.host.com:5001?adapter=local1&port=4001&mode=caller +``` + +- In **RENDEZVOUS** mode: same as **CALLER** except that the local +port, if not specified by the **port** parameter, defaults to the +value of the remote port (specified in the *port* part in the URI). + +```yaml +srt://remote.host.com:5001?mode=rendezvous +``` +(uses `remote.host.com` port 5001 for a remote host and the default +network device for routing to this host; the connection from the peer is +expected on that device and port 5001) + + +```yaml +srt://remote.host.com:5001?port=4001&adapter=local1 +``` +(uses `remote.host.com` port 5001 for a remote host and the peer +is expected to connect to `local1` address and port 4001) + **IMPORTANT** information about IPv6. @@ -238,14 +271,14 @@ the following restrictions: 2. In listener mode, if you leave the host empty, the socket is bound to `INADDR_ANY` for IPv4 only. If you want to make it listen on IPv6, you need to specify the host as `::`. -NOTE: Don't use square brackets syntax in the adapter specification, -as in this case only the host is expected. +NOTE: Don't use square brackets syntax in the **adapter** parameter +specification, as in this case only the host is expected. 3. If you want to listen for connections from both IPv4 and IPv6, mind the `ipv6only` option. The default value for this option is system default (see system manual for `IPV6_V6ONLY` socket option); if unsure, you might want to enforce `ipv6only=0` in order to be able to accept both IPv4 and IPv6 -connections in the same listener. +connections by the same listener, or set `ipv6only=1` to accept exclusively IPv6. 4. In rendezvous mode you may only interconnect both parties using IPv4, or both using IPv6. Unlike listener mode, if you want to leave the socket @@ -271,21 +304,22 @@ Examples: connection with local address `inaddr6_any` (IPv6) and port 4000 to a destination with port 5000. -* `srt://[::1]:5000?adapter=127.0.0.1&mode=rendezvous` - this URI is invalid - (different IP versions for binding and target address) +* `srt://[::1]:5000?adapter=127.0.0.1` - this URI is invalid + (different IP versions for binding and target address in rendezvous mode) -Some parameters handled for SRT medium are specific, all others are socket options. The following parameters are handled special way by `srt-live-transmit`: +Some parameters handled for SRT medium are specific, all others are socket options. The following parameters are handled in a special way by `srt-live-transmit`: - **mode**: enforce caller, listener or rendezvous mode -- **port**: enforce the **outgoing** port (the port number that will be set in the UDP packet as a source port when sent from this host). This can be used only in **caller mode**. +- **port**: enforce the **outgoing** port (the port number that will be set in the UDP packet as a source port when sent from this host). Not used in **listener** mode. - **blocking**: sets the `SRTO_RCVSYN` for input medium or `SRTO_SNDSYN` for output medium - **timeout**: sets `SRTO_RCVTIMEO` for input medium or `SRTO_SNDTIMEO` for output medium -- **adapter**: sets the adapter for listening in *listener* or *rendezvous* mode +- **adapter**: sets the local IP address to bind -All other parameters are SRT socket options. The following have the following value types: +All other parameters are SRT socket options. The Values column uses the +following type specification: - `bool`. Possible values: `yes`/`no`, `on`/`off`, `true`/`false`, `1`/`0`. -- `bytes` positive integer [1; INT32_MAX]. +- `bytes` positive integer `[1; INT32_MAX]`. - `ms` - positive integer value of milliseconds. | URI param | Values | SRT Option | Description | From 3558cd0dc4b3abf0de5c047ee233e0e91363dffa Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 14 Dec 2021 21:41:53 +0800 Subject: [PATCH 320/790] [core] Fix GC stop handling (#1950) 1. Protect m_bClosing by m_GCStopLock 2. Fix the issue that m_GCStopLock can be set up multiple times and never be released. srt_startup()/srt_cleanup() meant to be called only once, but it isn't used this way in FFmpeg/VLC/GStreamer. --- srtcore/api.cpp | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 8e18bd23e..7e831391b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -196,6 +196,8 @@ srt::CUDTUnited::CUDTUnited(): // XXX An unlikely exception thrown from the below calls // might destroy the application before `main`. This shouldn't // be a problem in general. + setupMutex(m_GCStopLock, "GCStop"); + setupCond(m_GCStopCond, "GCStop"); setupMutex(m_GlobControlLock, "GlobControl"); setupMutex(m_IDLock, "ID"); setupMutex(m_InitLock, "Init"); @@ -216,6 +218,16 @@ srt::CUDTUnited::~CUDTUnited() releaseMutex(m_GlobControlLock); releaseMutex(m_IDLock); releaseMutex(m_InitLock); + // XXX There's some weird bug here causing this + // to hangup on Windows. This might be either something + // bigger, or some problem in pthread-win32. As this is + // the application cleanup section, this can be temporarily + // tolerated with simply exit the application without cleanup, + // counting on that the system will take care of it anyway. +#ifndef _WIN32 + releaseCond(m_GCStopCond); +#endif + releaseMutex(m_GCStopLock); delete m_pCache; } @@ -254,15 +266,6 @@ int srt::CUDTUnited::startup() m_bClosing = false; - try - { - setupMutex(m_GCStopLock, "GCStop"); - setupCond(m_GCStopCond, "GCStop"); - } - catch (...) - { - return -1; - } if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) return -1; @@ -291,7 +294,10 @@ int srt::CUDTUnited::cleanup() if (!m_bGCStatus) return 0; - m_bClosing = true; + { + UniqueLock gclock(m_GCStopLock); + m_bClosing = true; + } // NOTE: we can do relaxed signaling here because // waiting on m_GCStopCond has a 1-second timeout, // after which the m_bClosing flag is cheched, which @@ -300,16 +306,6 @@ int srt::CUDTUnited::cleanup() CSync::signal_relaxed(m_GCStopCond); m_GCThread.join(); - // XXX There's some weird bug here causing this - // to hangup on Windows. This might be either something - // bigger, or some problem in pthread-win32. As this is - // the application cleanup section, this can be temporarily - // tolerated with simply exit the application without cleanup, - // counting on that the system will take care of it anyway. -#ifndef _WIN32 - releaseCond(m_GCStopCond); -#endif - m_bGCStatus = false; // Global destruction code From 5f7bc23f728b1fd5e5ca8e9c4b4b3ba6f3df7b59 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 20 Dec 2021 10:15:10 +0100 Subject: [PATCH 321/790] [core] Refactored the core stats structure (#2212). Group stats are moved to the new structure. --- srtcore/common.cpp | 7 - srtcore/common.h | 86 ------------ srtcore/core.cpp | 292 +++++++++++++-------------------------- srtcore/core.h | 66 +-------- srtcore/filelist.maf | 1 + srtcore/group.cpp | 40 +++--- srtcore/group.h | 30 ++-- srtcore/packetfilter.cpp | 9 +- srtcore/stats.h | 222 +++++++++++++++++++++++++++++ 9 files changed, 361 insertions(+), 392 deletions(-) create mode 100644 srtcore/stats.h diff --git a/srtcore/common.cpp b/srtcore/common.cpp index cd151116a..27dbe89d1 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -503,13 +503,6 @@ bool SrtParseConfig(string s, SrtConfig& w_config) return true; } -uint64_t PacketMetric::fullBytes() -{ - static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - return bytes + pkts * PKT_HDR_SIZE; -} - - namespace srt_logging { diff --git a/srtcore/common.h b/srtcore/common.h index 3e2ce9363..60a884f5c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1414,90 +1414,4 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(std::string s, srt::SrtConfig& w_config); -struct PacketMetric -{ - uint32_t pkts; - uint64_t bytes; - - void update(uint64_t size) - { - ++pkts; - bytes += size; - } - - void update(size_t mult, uint64_t value) - { - pkts += (uint32_t) mult; - bytes += mult * value; - } - - uint64_t fullBytes(); -}; - -template -struct MetricOp; - -template -struct MetricUsage -{ - METRIC_TYPE local; - METRIC_TYPE total; - - void Clear() - { - MetricOp::Clear(local); - } - - void Init() - { - MetricOp::Clear(total); - Clear(); - } - - void Update(uint64_t value) - { - local += value; - total += value; - } - - void UpdateTimes(size_t mult, uint64_t value) - { - local += mult * value; - total += mult * value; - } -}; - -template <> -inline void MetricUsage::Update(uint64_t value) -{ - local.update(value); - total.update(value); -} - -template <> -inline void MetricUsage::UpdateTimes(size_t mult, uint64_t value) -{ - local.update(mult, value); - total.update(mult, value); -} - -template -struct MetricOp -{ - static void Clear(METRIC_TYPE& m) - { - m = 0; - } -}; - -template <> -struct MetricOp -{ - static void Clear(PacketMetric& p) - { - p.pkts = 0; - p.bytes = 0; - } -}; - #endif diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fc86d8a98..98b412f4e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -875,49 +875,11 @@ void srt::CUDT::clearData() ScopedLock stat_lock(m_StatsLock); m_stats.tsStartTime = steady_clock::now(); - m_stats.sentTotal = m_stats.sentUniqTotal = m_stats.recvTotal = m_stats.recvUniqTotal - = m_stats.sndLossTotal = m_stats.rcvLossTotal = m_stats.retransTotal - = m_stats.sentACKTotal = m_stats.recvACKTotal = m_stats.sentNAKTotal = m_stats.recvNAKTotal = 0; - m_stats.tsLastSampleTime = steady_clock::now(); - m_stats.traceSent = m_stats.traceSentUniq = m_stats.traceRecv = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceReorderDistance = 0; - m_stats.traceBelatedTime = 0.0; - m_stats.traceRcvBelated = 0; - - m_stats.sndDropTotal = 0; - m_stats.traceSndDrop = 0; - m_stats.rcvDropTotal = 0; - m_stats.traceRcvDrop = 0; - - m_stats.m_rcvUndecryptTotal = 0; - m_stats.traceRcvUndecrypt = 0; - - m_stats.bytesSentTotal = 0; - m_stats.bytesSentUniqTotal = 0; - m_stats.bytesRecvTotal = 0; - m_stats.bytesRecvUniqTotal = 0; - m_stats.bytesRetransTotal = 0; - m_stats.traceBytesSent = 0; - m_stats.traceBytesSentUniq = 0; - m_stats.traceBytesRecv = 0; - m_stats.traceBytesRecvUniq = 0; - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; - - m_stats.traceBytesRetrans = 0; - m_stats.traceRcvBytesLoss = 0; - m_stats.sndBytesDropTotal = 0; - m_stats.rcvBytesDropTotal = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.m_rcvBytesUndecryptTotal = 0; - m_stats.traceRcvBytesUndecrypt = 0; + m_stats.sndr.reset(); + m_stats.rcvr.reset(); + m_stats.tsLastSampleTime = steady_clock::now(); + m_stats.traceReorderDistance = 0; m_stats.sndDuration = m_stats.m_sndDurationTotal = 0; } @@ -5579,14 +5541,10 @@ void * srt::CUDT::tsbpd(void *param) void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { - /* Update drop/skip stats */ enterCS(m_StatsLock); - m_stats.rcvDropTotal += seqlen; - m_stats.traceRcvDrop += seqlen; - /* Estimate dropped/skipped bytes from average payload */ + // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvBytesDropTotal += seqlen * avgpayloadsz; - m_stats.traceRcvBytesDrop += seqlen * avgpayloadsz; + m_stats.rcvr.dropped.count(stats::BytesPackets(seqlen * avgpayloadsz, (size_t) seqlen)); leaveCS(m_StatsLock); dropFromLossLists(lastack, CSeqNo::decseq(skiptoseqno)); //remove(from,to-inclusive) @@ -6401,10 +6359,7 @@ bool srt::CUDT::checkNeedDrop() if (dpkts > 0) { enterCS(m_StatsLock); - m_stats.traceSndDrop += dpkts; - m_stats.sndDropTotal += dpkts; - m_stats.traceSndBytesDrop += dbytes; - m_stats.sndBytesDropTotal += dbytes; + m_stats.sndr.dropped.count(stats::BytesPackets(dbytes, dpkts)); leaveCS(m_StatsLock); IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); @@ -7364,76 +7319,76 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) const steady_clock::time_point currtime = steady_clock::now(); perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); - perf->pktSent = m_stats.traceSent; - perf->pktSentUnique = m_stats.traceSentUniq; - perf->pktRecv = m_stats.traceRecv; - perf->pktRecvUnique = m_stats.traceRecvUniq; - perf->pktSndLoss = m_stats.traceSndLoss; - perf->pktRcvLoss = m_stats.traceRcvLoss; - perf->pktRetrans = m_stats.traceRetrans; - perf->pktRcvRetrans = m_stats.traceRcvRetrans; - perf->pktSentACK = m_stats.sentACK; - perf->pktRecvACK = m_stats.recvACK; - perf->pktSentNAK = m_stats.sentNAK; - perf->pktRecvNAK = m_stats.recvNAK; + perf->pktSent = m_stats.sndr.sent.trace.count(); + perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); + perf->pktRecv = m_stats.rcvr.recvd.trace.count(); + perf->pktRecvUnique = m_stats.rcvr.recvdUnique.trace.count(); + + perf->pktSndLoss = m_stats.sndr.lost.trace.count(); + perf->pktRcvLoss = m_stats.rcvr.lost.trace.count(); + perf->pktRetrans = m_stats.sndr.sentRetrans.trace.count(); + perf->pktRcvRetrans = m_stats.rcvr.recvdRetrans.trace.count(); + perf->pktSentACK = m_stats.rcvr.sentAck.trace.count(); + perf->pktRecvACK = m_stats.sndr.recvdAck.trace.count(); + perf->pktSentNAK = m_stats.rcvr.sentNak.trace.count(); + perf->pktRecvNAK = m_stats.sndr.recvdNak.trace.count(); perf->usSndDuration = m_stats.sndDuration; perf->pktReorderDistance = m_stats.traceReorderDistance; perf->pktReorderTolerance = m_iReorderTolerance; perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; - perf->pktRcvBelated = m_stats.traceRcvBelated; + perf->pktRcvBelated = m_stats.rcvr.recvdBelated.trace.count(); - perf->pktSndFilterExtra = m_stats.sndFilterExtra; - perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; - perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; - perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; + perf->pktSndFilterExtra = m_stats.sndr.sentFilterExtra.trace.count(); + perf->pktRcvFilterExtra = m_stats.rcvr.recvdFilterExtra.trace.count(); + perf->pktRcvFilterSupply = m_stats.rcvr.suppliedByFilter.trace.count(); + perf->pktRcvFilterLoss = m_stats.rcvr.lossFilter.trace.count(); /* perf byte counters include all headers (SRT+UDP+IP) */ - perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); - perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); - perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); - perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); - perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); - perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); - - perf->pktSndDrop = m_stats.traceSndDrop; - perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; - perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); - perf->byteRcvDrop = - m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; - perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; - perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; - - perf->pktSentTotal = m_stats.sentTotal; - perf->pktSentUniqueTotal = m_stats.sentUniqTotal; - perf->pktRecvTotal = m_stats.recvTotal; - perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; - perf->pktSndLossTotal = m_stats.sndLossTotal; - perf->pktRcvLossTotal = m_stats.rcvLossTotal; - perf->pktRetransTotal = m_stats.retransTotal; - perf->pktSentACKTotal = m_stats.sentACKTotal; - perf->pktRecvACKTotal = m_stats.recvACKTotal; - perf->pktSentNAKTotal = m_stats.sentNAKTotal; - perf->pktRecvNAKTotal = m_stats.recvNAKTotal; + perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr(); + perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr(); + perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr(); + perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr(); + perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr(); + perf->byteRcvLoss = m_stats.rcvr.recvd.trace.bytesWithHdr(); + + perf->pktSndDrop = m_stats.sndr.dropped.trace.count(); + perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count() + m_stats.rcvr.undecrypted.trace.count(); + perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(); + perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(); + perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count(); + perf->byteRcvUndecrypt = m_stats.rcvr.undecrypted.trace.bytes(); + + perf->pktSentTotal = m_stats.sndr.sent.total.count(); + perf->pktSentUniqueTotal = m_stats.sndr.sentUnique.total.count(); + perf->pktRecvTotal = m_stats.rcvr.recvd.total.count(); + perf->pktRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.count(); + perf->pktSndLossTotal = m_stats.sndr.lost.total.count(); + perf->pktRcvLossTotal = m_stats.rcvr.lost.total.count(); + perf->pktRetransTotal = m_stats.sndr.sentRetrans.total.count(); + perf->pktSentACKTotal = m_stats.rcvr.sentAck.total.count(); + perf->pktRecvACKTotal = m_stats.sndr.recvdAck.total.count(); + perf->pktSentNAKTotal = m_stats.rcvr.sentNak.total.count(); + perf->pktRecvNAKTotal = m_stats.sndr.recvdNak.total.count(); perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); - perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); - perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); - perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); - perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); - perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; - perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; - perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; - perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; - - perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); - perf->pktSndDropTotal = m_stats.sndDropTotal; - perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; - perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); - perf->byteRcvDropTotal = - m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; - perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; - perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; + perf->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr(); + perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr(); + perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr(); + perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr(); + perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr(); + perf->pktSndFilterExtraTotal = m_stats.sndr.sentFilterExtra.total.count(); + perf->pktRcvFilterExtraTotal = m_stats.rcvr.recvdFilterExtra.total.count(); + perf->pktRcvFilterSupplyTotal = m_stats.rcvr.suppliedByFilter.total.count(); + perf->pktRcvFilterLossTotal = m_stats.rcvr.lossFilter.total.count(); + + perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(); + perf->pktSndDropTotal = m_stats.sndr.dropped.total.count(); + perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count() + m_stats.rcvr.undecrypted.total.count(); + // TODO: The payload is dropped. Probably header sizes should not be counted? + perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(); + perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr() + m_stats.rcvr.undecrypted.total.bytesWithHdr(); + perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); + perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); // TODO: The following class members must be protected with a different mutex, not the m_StatsLock. const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); @@ -7454,29 +7409,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (clear) { - m_stats.traceSndDrop = 0; - m_stats.traceRcvDrop = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.traceRcvUndecrypt = 0; - m_stats.traceRcvBytesUndecrypt = 0; - m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; - m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; - m_stats.traceSent = m_stats.traceRecv - = m_stats.traceSentUniq = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.sndDuration = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceRcvBelated = 0; - m_stats.traceRcvBytesLoss = 0; - - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; + m_stats.sndr.resetTrace(); + m_stats.rcvr.resetTrace(); + m_stats.sndDuration = 0; m_stats.tsLastSampleTime = currtime; } } @@ -7833,8 +7769,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); - ++m_stats.sentNAK; - ++m_stats.sentNAKTotal; + m_stats.rcvr.sentNak.count(1); leaveCS(m_StatsLock); } // Call with no arguments - get loss list from internal data. @@ -7855,8 +7790,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); - ++m_stats.sentNAK; - ++m_stats.sentNAKTotal; + m_stats.rcvr.sentNak.count(1); leaveCS(m_StatsLock); } @@ -8183,8 +8117,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck); enterCS(m_StatsLock); - ++m_stats.sentACK; - ++m_stats.sentACKTotal; + m_stats.rcvr.sentAck.count(1); leaveCS(m_StatsLock); } else @@ -8439,7 +8372,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // Suppose transmission is bidirectional if sender is also receiving // data packets. enterCS(m_StatsLock); - const bool bPktsReceived = m_stats.recvTotal != 0; + const bool bPktsReceived = m_stats.rcvr.recvd.total.count() != 0; leaveCS(m_StatsLock); if (bPktsReceived) // Transmission is bidirectional. @@ -8532,8 +8465,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ updateCC(TEV_ACK, EventVariant(ackdata_seqno)); enterCS(m_StatsLock); - ++m_stats.recvACK; - ++m_stats.recvACKTotal; + m_stats.sndr.recvdAck.count(1); leaveCS(m_StatsLock); } @@ -8719,8 +8651,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) } enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); } else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) @@ -8740,8 +8671,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) const int num = m_pSndLossList->insert(losslist[i], losslist[i]); enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); } } @@ -8764,8 +8694,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); enterCS(m_StatsLock); - ++m_stats.recvNAK; - ++m_stats.recvNAKTotal; + m_stats.sndr.recvdNak.count(1); leaveCS(m_StatsLock); } @@ -9231,10 +9160,7 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi ackguard.unlock(); enterCS(m_StatsLock); - ++m_stats.traceRetrans; - ++m_stats.retransTotal; - m_stats.traceBytesRetrans += payload; - m_stats.bytesRetransTotal += payload; + m_stats.sndr.sentRetrans.count(payload); leaveCS(m_StatsLock); // Despite the contextual interpretation of packet.m_iMsgNo around @@ -9298,8 +9224,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Stats { ScopedLock lg(m_StatsLock); - ++m_stats.sndFilterExtra; - ++m_stats.sndFilterExtraTotal; + m_stats.sndr.sentFilterExtra.count(1); } } else @@ -9500,17 +9425,9 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); enterCS(m_StatsLock); - m_stats.traceBytesSent += payload; - m_stats.bytesSentTotal += payload; - ++m_stats.traceSent; - ++m_stats.sentTotal; + m_stats.sndr.sent.count(payload); if (new_packet_packed) - { - ++m_stats.traceSentUniq; - ++m_stats.sentUniqTotal; - m_stats.traceBytesSentUniq += payload; - m_stats.bytesSentUniqTotal += payload; - } + m_stats.sndr.sentUnique.count(payload); leaveCS(m_StatsLock); if (probe) @@ -9707,7 +9624,7 @@ int srt::CUDT::processData(CUnit* in_unit) { // This packet was retransmitted enterCS(m_StatsLock); - m_stats.traceRcvRetrans++; + m_stats.rcvr.recvdRetrans.count(packet.getLength()); leaveCS(m_StatsLock); #if ENABLE_HEAVY_LOGGING @@ -9767,10 +9684,7 @@ int srt::CUDT::processData(CUnit* in_unit) m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted); enterCS(m_StatsLock); - m_stats.traceBytesRecv += pktsz; - m_stats.bytesRecvTotal += pktsz; - ++m_stats.traceRecv; - ++m_stats.recvTotal; + m_stats.rcvr.recvd.count(pktsz); leaveCS(m_StatsLock); loss_seqs_t filter_loss_seqs; @@ -9809,14 +9723,11 @@ int srt::CUDT::processData(CUnit* in_unit) // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { + const int loss = diff - 1; // loss is all that is above diff == 1 ScopedLock lg(m_StatsLock); - int loss = diff - 1; // loss is all that is above diff == 1 - m_stats.traceRcvLoss += loss; - m_stats.rcvLossTotal += loss; const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - const uint64_t lossbytes = loss * avgpayloadsz; - m_stats.traceRcvBytesLoss += lossbytes; - m_stats.rcvBytesLossTotal += lossbytes; + m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, loss)); + HLOGC(qrlog.Debug, log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); @@ -9927,7 +9838,7 @@ int srt::CUDT::processData(CUnit* in_unit) enterCS(m_StatsLock); m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.traceRcvBelated++; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" @@ -10006,22 +9917,13 @@ int srt::CUDT::processData(CUnit* in_unit) EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; if (rc != ENCS_CLEAR) { - // Could not decrypt - // Keep packet in received buffer - // Crypto flags are still set - // It will be acknowledged - { - ScopedLock lg(m_StatsLock); - m_stats.traceRcvUndecrypt += 1; - m_stats.traceRcvBytesUndecrypt += pktsz; - m_stats.m_rcvUndecryptTotal += 1; - m_stats.m_rcvBytesUndecryptTotal += pktsz; - } - - // Log message degraded to debug because it may happen very often + // Heavy log message because if seen once the message may happen very often. HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); adding_successful = false; IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + ScopedLock lg(m_StatsLock); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); } } } @@ -10029,10 +9931,7 @@ int srt::CUDT::processData(CUnit* in_unit) if (adding_successful) { ScopedLock statslock(m_StatsLock); - ++m_stats.traceRecvUniq; - ++m_stats.recvUniqTotal; - m_stats.traceBytesRecvUniq += u->m_Packet.getLength(); - m_stats.bytesRecvUniqTotal += u->m_Packet.getLength(); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); } #if ENABLE_HEAVY_LOGGING @@ -11247,8 +11146,7 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) if (num > 0) { enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); HLOGC(xtlog.Debug, diff --git a/srtcore/core.h b/srtcore/core.h index 71aef9f0a..08056fec7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -73,6 +73,8 @@ modified by #include "utilities.h" #include "logger_defs.h" +#include "stats.h" + #include @@ -1065,73 +1067,17 @@ class CUDT struct CoreStats { time_point tsStartTime; // timestamp when the UDT entity is started - int64_t sentTotal; // total number of sent data packets, including retransmissions - int64_t sentUniqTotal; // total number of sent data packets, excluding rexmit and filter control - int64_t recvTotal; // total number of received packets - int64_t recvUniqTotal; // total number of received and delivered packets - int sndLossTotal; // total number of lost packets (sender side) - int rcvLossTotal; // total number of lost packets (receiver side) - int retransTotal; // total number of retransmitted packets - int sentACKTotal; // total number of sent ACK packets - int recvACKTotal; // total number of received ACK packets - int sentNAKTotal; // total number of sent NAK packets - int recvNAKTotal; // total number of received NAK packets - int sndDropTotal; - int rcvDropTotal; - uint64_t bytesSentTotal; // total number of bytes sent, including retransmissions - uint64_t bytesSentUniqTotal; // total number of bytes sent, including retransmissions - uint64_t bytesRecvTotal; // total number of received bytes - uint64_t bytesRecvUniqTotal; // total number of received bytes - uint64_t rcvBytesLossTotal; // total number of loss bytes (estimate) - uint64_t bytesRetransTotal; // total number of retransmitted bytes - uint64_t sndBytesDropTotal; - uint64_t rcvBytesDropTotal; - int m_rcvUndecryptTotal; - uint64_t m_rcvBytesUndecryptTotal; - - int sndFilterExtraTotal; - int rcvFilterExtraTotal; - int rcvFilterSupplyTotal; - int rcvFilterLossTotal; + stats::Sender sndr; // sender statistics + stats::Receiver rcvr; // receiver statistics int64_t m_sndDurationTotal; // total real time for sending time_point tsLastSampleTime; // last performance sample time - int64_t traceSent; // number of packets sent in the last trace interval - int64_t traceSentUniq; // number of original packets sent in the last trace interval - int64_t traceRecv; // number of packets received in the last trace interval - int64_t traceRecvUniq; // number of packets received AND DELIVERED in the last trace interval - int traceSndLoss; // number of lost packets in the last trace interval (sender side) - int traceRcvLoss; // number of lost packets in the last trace interval (receiver side) - int traceRetrans; // number of retransmitted packets in the last trace interval - int sentACK; // number of ACKs sent in the last trace interval - int recvACK; // number of ACKs received in the last trace interval - int sentNAK; // number of NAKs sent in the last trace interval - int recvNAK; // number of NAKs received in the last trace interval - int traceSndDrop; - int traceRcvDrop; - int traceRcvRetrans; int traceReorderDistance; double traceBelatedTime; - int64_t traceRcvBelated; - uint64_t traceBytesSent; // number of bytes sent in the last trace interval - uint64_t traceBytesSentUniq; // number of bytes sent in the last trace interval - uint64_t traceBytesRecv; // number of bytes sent in the last trace interval - uint64_t traceBytesRecvUniq; // number of bytes sent in the last trace interval - uint64_t traceRcvBytesLoss; // number of bytes bytes lost in the last trace interval (estimate) - uint64_t traceBytesRetrans; // number of bytes retransmitted in the last trace interval - uint64_t traceSndBytesDrop; - uint64_t traceRcvBytesDrop; - int traceRcvUndecrypt; - uint64_t traceRcvBytesUndecrypt; - - int sndFilterExtra; - int rcvFilterExtra; - int rcvFilterSupply; - int rcvFilterLoss; - + int64_t sndDuration; // real time for sending - time_point sndDurationCounter; // timers to record the sending Duration + time_point sndDurationCounter; // timers to record the sending Duration } m_stats; public: diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 16fb48d45..b800fdc44 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -72,6 +72,7 @@ queue.h congctl.h socketconfig.h srt_compat.h +stats.h threadname.h tsbpd_time.h utilities.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c9c01378a..b9c18090a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1692,7 +1692,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Now that at least one link has succeeded, update sending stats. - m_stats.sent.Update(len); + m_stats.sent.count(len); // Pity that the blocking mode only determines as to whether this function should // block or not, but the epoll flags must be updated regardless of the mode. @@ -2259,7 +2259,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) pos->packet.clear(); // Update stats as per delivery - m_stats.recv.Update(len); + m_stats.recv.count(len); updateAvgPayloadSize(len); // We predict to have only one packet ahead, others are pending to be reported by tsbpd. @@ -2491,7 +2491,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " BEHIND base=%" << m_RcvBaseSeqNo << " - discarding"); // The sequence is recorded, the packet has to be discarded. - m_stats.recvDiscard.Update(stat); + m_stats.recvDiscard.count(stat); continue; } @@ -2527,7 +2527,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) fillGroupData((w_mc), mctrl); // Update stats as per delivery - m_stats.recv.Update(output_size); + m_stats.recv.count(output_size); updateAvgPayloadSize(output_size); // Record, but do not update yet, until all sockets are handled. @@ -2681,7 +2681,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) const int32_t jump = (CSeqNo(slowest_kangaroo->second.mctrl.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; if (jump > 0) { - m_stats.recvDrop.UpdateTimes(jump, avgRcvPacketSize()); + m_stats.recvDrop.count(stats::BytesPackets(jump * static_cast(avgRcvPacketSize()), jump)); LOGC(grlog.Warn, log << "@" << m_GroupID << " GROUP RCV-DROPPED " << jump << " packet(s): seqno %" << m_RcvBaseSeqNo << " to %" << slowest_kangaroo->second.mctrl.pktseq); @@ -2703,7 +2703,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) pkt.clear(); // Update stats as per delivery - m_stats.recv.Update(len); + m_stats.recv.count(len); updateAvgPayloadSize(len); // It is unlikely to have a packet ahead because usually having one packet jumped-ahead @@ -2830,21 +2830,21 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) perf->msTimeStamp = count_milliseconds(currtime - m_tsStartTime); - perf->pktSentUnique = m_stats.sent.local.pkts; - perf->pktRecvUnique = m_stats.recv.local.pkts; - perf->pktRcvDrop = m_stats.recvDrop.local.pkts; + perf->pktSentUnique = m_stats.sent.trace.count(); + perf->pktRecvUnique = m_stats.recv.trace.count(); + perf->pktRcvDrop = m_stats.recvDrop.trace.count(); - perf->byteSentUnique = m_stats.sent.local.fullBytes(); - perf->byteRecvUnique = m_stats.recv.local.fullBytes(); - perf->byteRcvDrop = m_stats.recvDrop.local.fullBytes(); + perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(); + perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(); + perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(); - perf->pktSentUniqueTotal = m_stats.sent.total.pkts; - perf->pktRecvUniqueTotal = m_stats.recv.total.pkts; - perf->pktRcvDropTotal = m_stats.recvDrop.total.pkts; + perf->pktSentUniqueTotal = m_stats.sent.total.count(); + perf->pktRecvUniqueTotal = m_stats.recv.total.count(); + perf->pktRcvDropTotal = m_stats.recvDrop.total.count(); - perf->byteSentUniqueTotal = m_stats.sent.total.fullBytes(); - perf->byteRecvUniqueTotal = m_stats.recv.total.fullBytes(); - perf->byteRcvDropTotal = m_stats.recvDrop.total.fullBytes(); + perf->byteSentUniqueTotal = m_stats.sent.total.bytesWithHdr(); + perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(); + perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(); const double interval = static_cast(count_microseconds(currtime - m_stats.tsLastSampleTime)); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; @@ -3165,7 +3165,7 @@ CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_ } enterCS(u.m_StatsLock); - const int64_t drop_total = u.m_stats.sndDropTotal; + const int64_t drop_total = u.m_stats.sndr.dropped.total.count(); leaveCS(u.m_StatsLock); const bool have_new_drops = d->pktSndDropTotal != drop_total; @@ -4039,7 +4039,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // At least one link has succeeded, update sending stats. - m_stats.sent.Update(len); + m_stats.sent.count(len); // Now fill in the socket table. Check if the size is enough, if not, // then set the pointer to NULL and set the correct size. diff --git a/srtcore/group.h b/srtcore/group.h index c61835e1b..99f1c0509 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -697,31 +697,29 @@ class CUDTGroup time_point tsActivateTime; // Time when this group sent or received the first data packet time_point tsLastSampleTime; // Time reset when clearing stats - MetricUsage sent; // number of packets sent from the application - MetricUsage recv; // number of packets delivered from the group to the application - MetricUsage - recvDrop; // number of packets dropped by the group receiver (not received from any member) - MetricUsage recvDiscard; // number of packets discarded as already delivered + stats::Metric sent; // number of packets sent from the application + stats::Metric recv; // number of packets delivered from the group to the application + stats::Metric recvDrop; // number of packets dropped by the group receiver (not received from any member) + stats::Metric recvDiscard; // number of packets discarded as already delivered void init() { tsActivateTime = srt::sync::steady_clock::time_point(); - sent.Init(); - recv.Init(); - recvDrop.Init(); - recvDiscard.Init(); - - reset(); + tsLastSampleTime = srt::sync::steady_clock::now(); + sent.reset(); + recv.reset(); + recvDrop.reset(); + recvDiscard.reset(); } void reset() { - sent.Clear(); - recv.Clear(); - recvDrop.Clear(); - recvDiscard.Clear(); - tsLastSampleTime = srt::sync::steady_clock::now(); + + sent.resetTrace(); + recv.resetTrace(); + recvDrop.resetTrace(); + recvDiscard.resetTrace(); } } m_stats; diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index dffb2a8ba..305af3547 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -138,8 +138,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo { // Packet not to be passthru, update stats ScopedLock lg(m_parent->m_StatsLock); - ++m_parent->m_stats.rcvFilterExtra; - ++m_parent->m_stats.rcvFilterExtraTotal; + m_parent->m_stats.rcvr.recvdFilterExtra.count(1); } // w_loss_seqs enters empty into this function and can be only filled here. XXX ASSERT? @@ -152,8 +151,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo if (dist > 0) { ScopedLock lg(m_parent->m_StatsLock); - m_parent->m_stats.rcvFilterLoss += dist; - m_parent->m_stats.rcvFilterLossTotal += dist; + m_parent->m_stats.rcvr.lossFilter.count(dist); } else { @@ -171,8 +169,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo InsertRebuilt(w_incoming, m_unitq); ScopedLock lg(m_parent->m_StatsLock); - m_parent->m_stats.rcvFilterSupply += nsupply; - m_parent->m_stats.rcvFilterSupplyTotal += nsupply; + m_parent->m_stats.rcvr.suppliedByFilter.count(nsupply); } // Now that all units have been filled as they should be, diff --git a/srtcore/stats.h b/srtcore/stats.h new file mode 100644 index 000000000..6d934b0a7 --- /dev/null +++ b/srtcore/stats.h @@ -0,0 +1,222 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_STATS_H +#define INC_SRT_STATS_H + +#include "platform_sys.h" +#include "packet.h" + +namespace srt +{ +namespace stats +{ + +class Packets +{ +public: + Packets() : m_count(0) {} + + Packets(uint32_t num) : m_count(num) {} + + void reset() + { + m_count = 0; + } + + Packets& operator+= (const Packets& other) + { + m_count += other.m_count; + return *this; + } + + uint32_t count() const + { + return m_count; + } + +private: + uint32_t m_count; +}; + +class BytesPackets +{ +public: + BytesPackets() + : m_bytes(0) + , m_packets(0) + {} + + BytesPackets(uint64_t bytes, size_t n = 1) + : m_bytes(bytes) + , m_packets(n) + {} + + BytesPackets& operator+= (const BytesPackets& other) + { + m_bytes += other.m_bytes; + m_packets += other.m_packets; + return *this; + } + +public: + void reset() + { + m_packets = 0; + m_bytes = 0; + } + + void count(uint64_t bytes, size_t n = 1) + { + m_packets += (uint32_t) n; + m_bytes += bytes; + } + + uint64_t bytes() const + { + return m_bytes; + } + + uint32_t count() const + { + return m_packets; + } + + uint64_t bytesWithHdr() const + { + static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + return m_bytes + m_packets * PKT_HDR_SIZE; + } + +private: + uint64_t m_bytes; + uint32_t m_packets; +}; + +template +struct Metric +{ + METRIC_TYPE trace; + METRIC_TYPE total; + + void count(METRIC_TYPE val) + { + trace += val; + total += val; + } + + void reset() + { + trace.reset(); + total.reset(); + } + + void resetTrace() + { + trace.reset(); + } +}; + +/// Sender-side statistics. +struct Sender +{ + Metric sent; + Metric sentUnique; + Metric sentRetrans; // The number of data packets retransmitted by the sender. + Metric lost; // The number of packets reported lost (including repeated reports) to the sender in NAKs. + Metric dropped; // The number of data packets dropped by the sender. + + Metric sentFilterExtra; // The number of packets generate by the packet filter and sent by the sender. + + Metric recvdAck; // The number of ACK packets received by the sender. + Metric recvdNak; // The number of ACK packets received by the sender. + + void reset() + { + sent.reset(); + sentUnique.reset(); + sentRetrans.reset(); + lost.reset(); + dropped.reset(); + recvdAck.reset(); + recvdNak.reset(); + sentFilterExtra.reset(); + } + + void resetTrace() + { + sent.resetTrace(); + sentUnique.resetTrace(); + sentRetrans.resetTrace(); + lost.resetTrace(); + dropped.resetTrace(); + recvdAck.resetTrace(); + recvdNak.resetTrace(); + sentFilterExtra.resetTrace(); + } +}; + +/// Receiver-side statistics. +struct Receiver +{ + Metric recvd; + Metric recvdUnique; + Metric recvdRetrans; // The number of retransmitted data packets received by the receiver. + Metric lost; // The number of packets detected by the receiver as lost. + Metric dropped; // The number of packets dropped by the receiver (as too-late to be delivered). + Metric recvdBelated; // The number of belated packets received (dropped as too late but eventually received). + Metric undecrypted; // The number of packets received by the receiver that failed to be decrypted. + + Metric recvdFilterExtra; // The number of filter packets (e.g. FEC) received by the receiver. + Metric suppliedByFilter; // The number of lost packets got from the packet filter at the receiver side (e.g. loss recovered by FEC). + Metric lossFilter; // The number of lost DATA packets not recovered by the packet filter at the receiver side. + + Metric sentAck; // The number of ACK packets sent by the receiver. + Metric sentNak; // The number of NACK packets sent by the receiver. + + void reset() + { + recvd.reset(); + recvdUnique.reset(); + recvdRetrans.reset(); + lost.reset(); + dropped.reset(); + recvdBelated.reset(); + undecrypted.reset(); + recvdFilterExtra.reset(); + suppliedByFilter.reset(); + lossFilter.reset(); + sentAck.reset(); + sentNak.reset(); + } + + void resetTrace() + { + recvd.resetTrace(); + recvdUnique.resetTrace(); + recvdRetrans.resetTrace(); + lost.resetTrace(); + dropped.resetTrace(); + recvdBelated.resetTrace(); + undecrypted.resetTrace(); + recvdFilterExtra.resetTrace(); + suppliedByFilter.resetTrace(); + lossFilter.resetTrace(); + sentAck.resetTrace(); + sentNak.resetTrace(); + } +}; + +} // namespace stats +} // namespace srt + +#endif // INC_SRT_STATS_H + + From 1111cbd1f9212e0d7ef4fb8b26aa616a03754cb5 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 24 Dec 2021 12:56:24 +0100 Subject: [PATCH 322/790] [core] Fixed RCV TL drop of packets dropped by SND (#2214) Moved the TL drop logic from the TSBPD thread to a dedicated function. --- docs/API/statistics.md | 2 ++ srtcore/buffer_rcv.cpp | 9 ++++---- srtcore/buffer_rcv.h | 3 ++- srtcore/core.cpp | 51 ++++++++++++++++++++++++------------------ srtcore/core.h | 6 +++++ 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 334437bc7..7a3ee10db 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -235,6 +235,8 @@ where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds The total number of _dropped_ by the SRT receiver and, as a result, not delivered to the upstream application DATA packets (refer to [TLPKTDROP](https://github.com/Haivision/srt-rfc/blob/master/draft-sharabayko-mops-srt.md#too-late-packet-drop-too-late-packet-drop) mechanism). Available for receiver. This statistic counts + +- not arrived packets including those signalled for dropping by the sender, that were dropped in favor of the subsequent existing packets, - arrived too late packets (retransmitted or original packets arrived out of order), - arrived in time packets, but decrypted with errors (see also [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic). diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 0ef1d75de..014ccc0ea 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -159,7 +159,7 @@ int CRcvBufferNew::insert(CUnit* unit) return 0; } -void CRcvBufferNew::dropUpTo(int32_t seqno) +int CRcvBufferNew::dropUpTo(int32_t seqno) { // Can drop only when nothing to read, and // first unacknowledged packet is missing. @@ -173,17 +173,15 @@ void CRcvBufferNew::dropUpTo(int32_t seqno) if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); - return; + return 0; } - /*LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropUpTo(): seqno=" << seqno << ", pkts=" << len - << ". Buffer start " << m_iStartSeqNo << ".");*/ - m_iMaxPosInc -= len; if (m_iMaxPosInc < 0) m_iMaxPosInc = 0; // Check that all packets being dropped are missing. + const int iDropCnt = len; while (len > 0) { if (m_entries[m_iStartPos].pUnit != NULL) @@ -210,6 +208,7 @@ void CRcvBufferNew::dropUpTo(int32_t seqno) // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); + return iDropCnt; } void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 589cf2bc0..cee4329a6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -69,8 +69,9 @@ class CRcvBufferNew /// 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. /// - void dropUpTo(int32_t seqno); + int dropUpTo(int32_t seqno); /// @brief Drop the whole message from the buffer. /// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 98b412f4e..986e76b4a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5174,39 +5174,21 @@ void * srt::CUDT::tsbpd(void* param) rxready = true; if (info.seq_gap) { - const int seq_gap_len = CSeqNo::seqoff(self->m_iRcvLastSkipAck, info.seqno); - SRT_ASSERT(seq_gap_len > 0); - /*if (!info.seq_gap) - { - LOGC(brlog.Warn, log << "TSBPD worker: no gap. pktseqno=" << info.seqno - << ", m_iRcvLastSkipAck=" << self->m_iRcvLastSkipAck - << ", RBuffer start seqno=" << self->m_pRcvBuffer->getStartSeqNo() - << ", m_iRcvLastAck=" << self->m_iRcvLastAck - << ", init seqnoo=" << self->m_iISN); - }*/ - - // Drop too late packets - self->updateForgotten(seq_gap_len, self->m_iRcvLastSkipAck, info.seqno); - //if (info.seq_gap) // If there is no sequence gap, we are reading ahead of ACK. - //{ - self->m_pRcvBuffer->dropUpTo(info.seqno); - //} - - self->m_iRcvLastSkipAck = info.seqno; + const int iDropCnt SRT_ATR_UNUSED = self->dropTooLateUpTo(info.seqno); + #if ENABLE_EXPERIMENTAL_BONDING shall_update_group = true; #endif #if ENABLE_LOGGING const int64_t timediff_us = count_microseconds(tnow - info.tsbpd_time); - // TODO: seq_gap_len is not the actual number of packets dropped. #if ENABLE_HEAVY_LOGGING HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" - << seq_gap_len << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " + << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif - LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << seq_gap_len << " packet(s), packet seqno %" << info.seqno + LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif @@ -5539,6 +5521,30 @@ void * srt::CUDT::tsbpd(void *param) } #endif // ENABLE_NEW_RCVBUFFER +int srt::CUDT::dropTooLateUpTo(int seqno) +{ + const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); + + // seq_gap_len can be <= 0 if a packet has been dropped by the sender. + if (seq_gap_len > 0) + { + // Remove [from,to-inclusive] + dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); + m_iRcvLastSkipAck = seqno; + } + + const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); + if (iDropCnt > 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, (size_t) iDropCnt)); + leaveCS(m_StatsLock); + } + return iDropCnt; +} + void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { enterCS(m_StatsLock); @@ -7898,6 +7904,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // If there is no loss, the ACK is the current largest sequence number plus 1; // Otherwise it is the smallest sequence number in the receiver loss list. ScopedLock lock(m_RcvLossLock); + // TODO: Consider the Fresh Loss list as well!!! ack = m_pRcvLossList->getFirstLostSeq(); } diff --git a/srtcore/core.h b/srtcore/core.h index 08056fec7..8e3d7bc6d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -705,6 +705,12 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); + /// Drop too late packets. Updaet 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. + /// @return The number of packets dropped. + int dropTooLateUpTo(int seqno); + void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); From 3d26644e2b029b7da94713e1fd16e77006acc715 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Sun, 26 Dec 2021 11:44:17 +0100 Subject: [PATCH 323/790] [core] Fixed build with the old RCV buffer --- srtcore/core.cpp | 49 ++++++++++++++++++++++++------------------------ srtcore/core.h | 2 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 986e76b4a..6f5e17ff1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5302,6 +5302,31 @@ void * srt::CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); return NULL; } + +int srt::CUDT::dropTooLateUpTo(int seqno) +{ + const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); + + // seq_gap_len can be <= 0 if a packet has been dropped by the sender. + if (seq_gap_len > 0) + { + // Remove [from,to-inclusive] + dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); + m_iRcvLastSkipAck = seqno; + } + + const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); + if (iDropCnt > 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, (size_t)iDropCnt)); + leaveCS(m_StatsLock); + } + return iDropCnt; +} + #else void * srt::CUDT::tsbpd(void *param) { @@ -5521,30 +5546,6 @@ void * srt::CUDT::tsbpd(void *param) } #endif // ENABLE_NEW_RCVBUFFER -int srt::CUDT::dropTooLateUpTo(int seqno) -{ - const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); - - // seq_gap_len can be <= 0 if a packet has been dropped by the sender. - if (seq_gap_len > 0) - { - // Remove [from,to-inclusive] - dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); - m_iRcvLastSkipAck = seqno; - } - - const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); - if (iDropCnt > 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, (size_t) iDropCnt)); - leaveCS(m_StatsLock); - } - return iDropCnt; -} - void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { enterCS(m_StatsLock); diff --git a/srtcore/core.h b/srtcore/core.h index 8e3d7bc6d..adf35b35b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -705,11 +705,13 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); +#if ENABLE_NEW_RCVBUFFER /// Drop too late packets. Updaet 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. /// @return The number of packets dropped. int dropTooLateUpTo(int seqno); +#endif void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); From 258167de0500b3b26aa57da8f78cf025cd514fd2 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 12 Jan 2022 21:57:07 +0800 Subject: [PATCH 324/790] [core] replace ++ with incPos() in getTimespan_ms() --- srtcore/buffer_rcv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 014ccc0ea..6d783f0d3 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -507,7 +507,7 @@ int CRcvBufferNew::getTimespan_ms() const if (startpos == lastpos) break; - ++startpos; + startpos = incPos(startpos); } if (m_entries[startpos].pUnit == NULL) From 8afcdbeb4963ef3fe50f15d147359902fec38548 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 12 Jan 2022 17:48:22 +0800 Subject: [PATCH 325/790] [core] fix m_iMaxPosInc was not updated in releaseNextFillerEntries() --- srtcore/buffer_rcv.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 6d783f0d3..af3ab7f0c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -614,6 +614,9 @@ void CRcvBufferNew::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; + --m_iMaxPosInc; + if (m_iMaxPosInc < 0) + m_iMaxPosInc = 0; } } From 2aa90bb2bd89d93ea90ff2339af089e7396a6041 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 17 Jan 2022 09:37:15 +0530 Subject: [PATCH 326/790] [build] CMake: fix system library names for MinGW (#2219) .lib extension is used in MSVC and MSVC-like toolchain. In MinGW environment, .dll.a extension is used for system import libraries. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d56c1200..55fecf541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -906,7 +906,7 @@ if (srt_libspec_shared) set_target_properties(${TARGET_srt}_shared PROPERTIES LINK_FLAGS "/DELAYLOAD:libeay32.dll") endif() elseif (MINGW) - target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) + target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32 ws2_32) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) endif() From 31de8aa56a4d276a5a608ae828bfb77271223de4 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Mon, 17 Jan 2022 20:53:08 +0800 Subject: [PATCH 327/790] [core] Add CRcvBufferNew::dropUnitInPos(..) (#2226) to simplify dropUpTo(..) and dropMessage(..) --- srtcore/buffer_rcv.cpp | 18 ++++++++++++++++++ srtcore/buffer_rcv.h | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index af3ab7f0c..a41e34329 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -605,6 +605,24 @@ void CRcvBufferNew::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } +bool CRcvBufferNew::dropUnitInPos(int pos) +{ + if (!m_entries[pos].pUnit) + return false; + if (m_tsbpd.isEnabled()) + { + updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + } + else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + { + --m_numOutOfOrderPackets; + if (pos == m_iFirstReadableOutOfOrder) + m_iFirstReadableOutOfOrder = -1; + } + releaseUnitInPos(pos); + return true; +} + void CRcvBufferNew::releaseNextFillerEntries() { int pos = m_iStartPos; diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index cee4329a6..0cba51972 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -221,6 +221,11 @@ class CRcvBufferNew void updateNonreadPos(); void releaseUnitInPos(int pos); + /// @brief Drop a unit from the buffer. + /// @param pos position in the m_entries of the unit to drop. + /// @return false if nothing to drop, true if the unit was dropped successfully. + bool dropUnitInPos(int pos); + /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). void releaseNextFillerEntries(); From 24bf666d1753026de7a18220f08183d667984778 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 Jan 2022 11:21:05 +0800 Subject: [PATCH 328/790] [core] CRcvBufferNew::dropUpTo() able to drop non-empty units (#2221) Using fixed TSBPD timebase update (#2226) when dropping. --- srtcore/buffer_rcv.cpp | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a41e34329..528bc1227 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -161,15 +161,10 @@ int CRcvBufferNew::insert(CUnit* unit) int CRcvBufferNew::dropUpTo(int32_t seqno) { - // Can drop only when nothing to read, and - // first unacknowledged packet is missing. - SRT_ASSERT(m_iStartPos == m_iFirstNonreadPos); - IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); - SRT_ASSERT(len > 0); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -180,21 +175,11 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) if (m_iMaxPosInc < 0) m_iMaxPosInc = 0; - // Check that all packets being dropped are missing. const int iDropCnt = len; while (len > 0) { - if (m_entries[m_iStartPos].pUnit != NULL) - { - releaseUnitInPos(m_iStartPos); - } - - if (m_entries[m_iStartPos].status != EntryState_Empty) - { - SRT_ASSERT(m_entries[m_iStartPos].status == EntryState_Drop || m_entries[m_iStartPos].status == EntryState_Read); - m_entries[m_iStartPos].status = EntryState_Empty; - } - + dropUnitInPos(m_iStartPos); + m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); m_iStartPos = incPos(m_iStartPos); --len; @@ -202,12 +187,14 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) // Update positions m_iStartSeqNo = seqno; - // Move forward if there are "read" entries. + // Move forward if there are "read/drop" entries. releaseNextFillerEntries(); // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + updateFirstReadableOutOfOrder(); return iDropCnt; } From 8c05c70d015c7d379823f389b5469901376ee5f4 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 Jan 2022 12:42:57 +0800 Subject: [PATCH 329/790] [core] Fix CRcvBufferNew::dropMessage() (#2222) (revise buffer state after drop) --- srtcore/buffer_rcv.cpp | 71 ++++++++++++++++++++++++++++++++++++++---- srtcore/buffer_rcv.h | 3 ++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 528bc1227..7e6e5a830 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -206,6 +206,7 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno != 0) { + int minDroppedOffset = -1; for (int i = m_iStartPos; i != end_pos; i = incPos(i)) { // TODO: Maybe check status? @@ -215,11 +216,26 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { - releaseUnitInPos(i); + dropUnitInPos(i); m_entries[i].status = EntryState_Drop; + if (minDroppedOffset == -1) + minDroppedOffset = offPos(m_iStartPos, i); } } - + // Check if units before m_iFirstNonreadPos are dropped. + bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); + releaseNextFillerEntries(); + if (needUpdateNonreadPos) + { + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); + } + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + { + if (!checkFirstReadableOutOfOrder()) + m_iFirstReadableOutOfOrder = -1; + updateFirstReadableOutOfOrder(); + } return; } @@ -235,17 +251,32 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int start_off = max(0, offset_a); const int last_pos = incPos(m_iStartPos, offset_b); + int minDroppedOffset = -1; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { - if (m_entries[i].pUnit) - { - releaseUnitInPos(i); - } + dropUnitInPos(i); m_entries[i].status = EntryState_Drop; + if (minDroppedOffset == -1) + minDroppedOffset = offPos(m_iStartPos, i); } LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; " << seqnohi << "]."); + + // Check if units before m_iFirstNonreadPos are dropped. + bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); + releaseNextFillerEntries(); + if (needUpdateNonreadPos) + { + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); + } + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + { + if (!checkFirstReadableOutOfOrder()) + m_iFirstReadableOutOfOrder = -1; + updateFirstReadableOutOfOrder(); + } } int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) @@ -720,6 +751,34 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) return; } +bool CRcvBufferNew::checkFirstReadableOutOfOrder() +{ + if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + return false; + + const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + int msgno = -1; + for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + { + if (!m_entries[pos].pUnit) + return false; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + if (pkt.getMsgOrderFlag()) + return false; + + if (msgno == -1) + msgno = pkt.getMsgSeq(m_bPeerRexmitFlag); + else if (msgno != pkt.getMsgSeq(m_bPeerRexmitFlag)) + return false; + + if (pkt.getMsgBoundary() & PB_LAST) + return true; + } + + return false; +} + void CRcvBufferNew::updateFirstReadableOutOfOrder() { if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 0cba51972..f7e01d930 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -215,6 +215,7 @@ class CRcvBufferNew private: inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } + inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : (m_szSize + pos2 - pos1); } private: void countBytes(int pkts, int bytes); @@ -237,6 +238,8 @@ class CRcvBufferNew /// Scan for availability of out of order packets. void onInsertNotInOrderPacket(int insertpos); + // Check if m_iFirstReadableOutOfOrder is still readable. + bool checkFirstReadableOutOfOrder(); void updateFirstReadableOutOfOrder(); int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; From 85185585b6af006dcb31c0cd17040e5d141fe1fb Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 11 Jan 2022 17:13:42 +0800 Subject: [PATCH 330/790] [core] fixed missing m_RcvBufferLock in processCtrlDropReq() --- srtcore/core.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6f5e17ff1..4a5486b82 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8824,11 +8824,14 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { UniqueLock rlock(m_RecvLock); const bool using_rexmit_flag = m_bPeerRexmitFlag; + { + ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER - m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); #else - m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); #endif + } // When the drop request was received, it means that there are // packets for which there will never be ACK sent; if the TSBPD thread // is currently in the ACK-waiting state, it may never exit. From a31e618e93ffa80afe57dfef0ea7d590ac380c3a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 16:45:17 +0700 Subject: [PATCH 331/790] [core] Refactoring: added packUniqueData(..) func --- srtcore/buffer.cpp | 37 +++---- srtcore/core.cpp | 256 +++++++++++++++++++++---------------------- srtcore/core.h | 16 ++- srtcore/queue.cpp | 4 +- test/test_buffer.cpp | 2 +- 5 files changed, 154 insertions(+), 161 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index d07080981..4d95fe6ca 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -410,30 +410,19 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.setLength(readlen); w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; - // XXX This is probably done because the encryption should happen - // just once, and so this sets the encryption flags to both msgno bitset - // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption - // happen at the time when scheduling a new packet to send, but the packet - // must remain in the send buffer until it's ACKed. For the case of rexmit - // the packet will be taken "as is" (that is, already encrypted). - // - // The problem is in the order of things: - // 0. When the application stores the data, some of the flags for PH_MSGNO are set. - // 1. The readData() is called to get the original data sent by the application. - // 2. The data are original and must be encrypted. They WILL BE encrypted, later. - // 3. So far we are in readData() so the encryption flags must be updated NOW because - // later we won't have access to the block's data. - // 4. After exiting from readData(), the packet is being encrypted. It's immediately - // sent, however the data must remain in the sending buffer until they are ACKed. - // 5. In case when rexmission is needed, the second overloaded version of readData - // is being called, and the buffer + PH_MSGNO value is extracted. All interesting - // flags must be present and correct at that time. - // - // The only sensible way to fix this problem is to encrypt the packet not after - // extracting from here, but when the packet is stored into CSndBuffer. The appropriate - // flags for PH_MSGNO will be applied directly there. Then here the value for setting - // PH_MSGNO will be set as is. - + // 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. + // The payload must be encrypted for the first time if the encryption + // is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet + // header must be set and remembered accordingly (see EncryptionKeySpec). + // 3. The next time this packet is read (only for retransmission), the payload is already + // encrypted, and the proper flag value is already stored. + + // TODO: Alternatively, encryption could happen before the packet is submitted to the buffer + // (before the addBuffer() call), and corresponding flags could be set accordingly. + // This may also put an encryption burden on the application thread, rather than the sending thread, + // which could be more efficient. Note that packet sequence number must be properly set in that case, + // as it is used as a counter for the AES encryption. if (kflgs == -1) { HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4a5486b82..483836599 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9189,7 +9189,7 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi return 0; } -std::pair srt::CUDT::packData(CPacket& w_packet) +std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; bool probe = false; @@ -9197,8 +9197,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) bool new_packet_packed = false; bool filter_ctl_pkt = false; - int kflg = EK_NOENC; - const steady_clock::time_point enter_time = steady_clock::now(); if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime) @@ -9217,7 +9215,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // start the dissolving process, this process will // not be started until this function is finished. if (!m_bOpened) - return std::make_pair(0, enter_time); + return std::make_pair(false, enter_time); payload = packLostData((w_packet), (origintime)); if (payload > 0) @@ -9233,122 +9231,24 @@ std::pair srt::CUDT::packData(CPacket& w_packet) filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set // Stats - { - ScopedLock lg(m_StatsLock); - m_stats.sndr.sentFilterExtra.count(1); - } + ScopedLock lg(m_StatsLock); + m_stats.sndr.sentFilterExtra.count(1); } else { - // If no loss, and no packetfilter control packet, pack a new packet. - - // Check the congestion/flow window limit - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); - const int flightspan = getFlightSpan(); - if (cwnd > flightspan) - { - // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the - // send buffer. This should be somehow avoided, the crypto flags should be set - // together with encrypting, and the packet should be sent as is, when rexmitting. - // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field - // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. - kflg = m_pCryptoControl->getSndCryptoFlags(); - int pktskipseqno = 0; - payload = m_pSndBuffer->readData((w_packet), (origintime), kflg, (pktskipseqno)); - if (pktskipseqno) - { - // Some packets were skipped due to TTL expiry. - m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); - } - - if (payload) - { - // A CHANGE. The sequence number is currently added to the packet - // when scheduling, not when extracting. This is a inter-migration form, - // so still override the value, but trace it. - m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); - - // Do this checking only for groups and only at the very first moment, - // when there's still nothing in the buffer. Otherwise there will be - // a serious data discrepancy between the agent and the peer. - // After increasing by 1, but being previously set as ISN-1, this should be == ISN, - // if this is the very first packet to send. -#if ENABLE_EXPERIMENTAL_BONDING - // Fortunately here is only the procedure that verifies if the extraction - // sequence is moved due to the difference between ISN caught during the existing - // transmission and the first sequence possible to be used at the first sending - // instruction. The group itself isn't being accessed. - if (m_parent->m_GroupOf && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) - { - const int packetspan = CSeqNo::seqcmp(w_packet.m_iSeqNo, m_iSndCurrSeqNo); - - HLOGC(qslog.Debug, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo - << " DIFF: " << packetspan << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); - - // This is the very first packet to be sent; so there's nothing in - // the sending buffer yet, and therefore we are in a situation as just - // after connection. No packets in the buffer, no packets are sent, - // no ACK to be awaited. We can screw up all the variables that are - // initialized from ISN just after connection. - // - // Additionally send the drop request to the peer so that it - // won't stupidly request the packets to be retransmitted. - // Don't do it if the difference isn't positive or exceeds the threshold. - if (packetspan > 0) - { - int32_t seqpair[2]; - seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = w_packet.m_iSeqNo; - HLOGC(qslog.Debug, log << "... sending INITIAL DROP (ISN FIX): " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" - << seqpair[0] << " - " << seqpair[1] << "(" << packetspan << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); - - // In case when this message is lost, the peer will still get the - // UMSG_DROPREQ message when the agent realizes that the requested - // packet are not present in the buffer (preadte the send buffer). - } - } - else -#endif - { - HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo - << " DIFF: " << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) - << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); - -#if ENABLE_EXPERIMENTAL_BONDING - HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_GroupOf ? "yes":"no") - << " extraction-seq=" << m_iSndCurrSeqNo << " scheduling-seq=" << w_packet.m_iSeqNo << " ISN=" << m_iISN); -#endif - - // Do this always when not in a group, - w_packet.m_iSeqNo = m_iSndCurrSeqNo; - } - - // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) - probe = true; - - new_packet_packed = true; - } - else - { - m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = steady_clock::duration(); - return std::make_pair(0, enter_time); - } - } - else + if (!packUniqueData(w_packet, origintime)) { - HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); m_tsNextSendTime = steady_clock::time_point(); m_tdSendTimeDiff = steady_clock::duration(); - return std::make_pair(0, enter_time); + return std::make_pair(false, enter_time); } + new_packet_packed = true; + + // every 16 (0xF) packets, a packet pair is sent + if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + probe = true; + payload = (int) w_packet.getLength(); reason = "normal"; } @@ -9389,23 +9289,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) w_packet.m_iID = m_PeerID; - /* Encrypt if 1st time this packet is sent and crypto is enabled */ - if (kflg) - { - // XXX Encryption flags are already set on the packet before calling this. - // See readData() above. - if (m_pCryptoControl->encrypt((w_packet))) - { - // Encryption failed - //>>Add stats for crypto failure - LOGC(qslog.Warn, log << "ENCRYPT FAILED - packet won't be sent, size=" << payload); - // Encryption failed - return std::make_pair(-1, enter_time); - } - payload = (int) w_packet.getLength(); /* Cipher may change length */ - reason += " (encrypted)"; - } - if (new_packet_packed && m_PacketFilter) { HLOGC(qslog.Debug, log << "filter: Feeding packet for source clip"); @@ -9472,7 +9355,120 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #endif } - return std::make_pair(payload, m_tsNextSendTime); + return std::make_pair(payload >= 0, m_tsNextSendTime); +} + +bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) +{ + // Check the congestion/flow window limit + const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int flightspan = getFlightSpan(); + if (cwnd <= flightspan) + { + HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); + return false; + } + + // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the + // send buffer. This should be somehow avoided, the crypto flags should be set + // together with encrypting, and the packet should be sent as is, when rexmitting. + // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field + // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. + const int kflg = m_pCryptoControl->getSndCryptoFlags(); + int pktskipseqno = 0; + const int pld_size = m_pSndBuffer->readData((w_packet), (w_origintime), kflg, (pktskipseqno)); + if (pktskipseqno) + { + // Some packets were skipped due to TTL expiry. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); + } + + if (pld_size == 0) + { + return false; + } + + // A CHANGE. The sequence number is currently added to the packet + // when scheduling, not when extracting. This is a inter-migration form, + // so still override the value, but trace it. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); + + // Do this checking only for groups and only at the very first moment, + // when there's still nothing in the buffer. Otherwise there will be + // a serious data discrepancy between the agent and the peer. + // After increasing by 1, but being previously set as ISN-1, this should be == ISN, + // if this is the very first packet to send. +#if ENABLE_EXPERIMENTAL_BONDING + // Fortunately here is only the procedure that verifies if the extraction + // sequence is moved due to the difference between ISN caught during the existing + // transmission and the first sequence possible to be used at the first sending + // instruction. The group itself isn't being accessed. + if (m_parent->m_GroupOf && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) + { + const int packetspan = CSeqNo::seqcmp(w_packet.m_iSeqNo, m_iSndCurrSeqNo); + + HLOGC(qslog.Debug, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo + << " from SCHEDULING sequence " << w_packet.m_iSeqNo + << " DIFF: " << packetspan << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); + + // This is the very first packet to be sent; so there's nothing in + // the sending buffer yet, and therefore we are in a situation as just + // after connection. No packets in the buffer, no packets are sent, + // no ACK to be awaited. We can screw up all the variables that are + // initialized from ISN just after connection. + // + // Additionally send the drop request to the peer so that it + // won't stupidly request the packets to be retransmitted. + // Don't do it if the difference isn't positive or exceeds the threshold. + if (packetspan > 0) + { + int32_t seqpair[2]; + seqpair[0] = m_iSndCurrSeqNo; + seqpair[1] = w_packet.m_iSeqNo; + HLOGC(qslog.Debug, log << "... sending INITIAL DROP (ISN FIX): " + << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" + << seqpair[0] << " - " << seqpair[1] << "(" << packetspan << " packets)"); + sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + + // In case when this message is lost, the peer will still get the + // UMSG_DROPREQ message when the agent realizes that the requested + // packet are not present in the buffer (preadte the send buffer). + } + } + else +#endif + { + HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo + << " over SCHEDULING sequence " << w_packet.m_iSeqNo + << " DIFF: " << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); + +#if ENABLE_EXPERIMENTAL_BONDING + HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_GroupOf ? "yes":"no") + << " extraction-seq=" << m_iSndCurrSeqNo << " scheduling-seq=" << w_packet.m_iSeqNo << " ISN=" << m_iISN); +#endif + + // Do this always when not in a group, + w_packet.m_iSeqNo = m_iSndCurrSeqNo; + } + + // Encrypt if 1st time this packet is sent and crypto is enabled + if (kflg != EK_NOENC) + { + // Note that the packet header must have a valid seqno set, as it is used as a counter for encryption. + // Other fields of the data packet header (e.g. timestamp, destination socket ID) are not used for the counter. + // Cypher may change packet length! + if (m_pCryptoControl->encrypt((w_packet))) + { + // Encryption failed + //>>Add stats for crypto failure + LOGC(qslog.Warn, log << "ENCRYPT FAILED - packet won't be sent, size=" << pld_size); + return -1; + } + } + + return true; } // This is a close request, but called from the diff --git a/srtcore/core.h b/srtcore/core.h index adf35b35b..c25e9c8c1 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1030,15 +1030,23 @@ class CUDT /// @return payload size on success, <=0 on failure int packLostData(CPacket &packet, time_point &origintime); + /// Pack a unique data packet (never sent so far) in CPacket for sending. + /// + /// @param packet [in, out] a CPacket structure to fill. + /// @param origintime [in, out] origin timestamp of the packet. + /// + /// @return true if a packet has been packets; false otherwise. + bool packUniqueData(CPacket& packet, time_point& origintime); + /// Pack in CPacket the next data to be send. /// /// @param packet [in, out] a CPacket structure to fill /// - /// @return A pair of values is returned (payload, timestamp). - /// The payload tells the size of the payload, packed in CPacket. + /// @return A pair of values is returned (is_payload_valid, timestamp). + /// If is_payload_valid is false, there was nothing packed for sending, + /// and the timestamp value should be ignored. /// The timestamp is the full source/origin timestamp of the data. - /// If payload is <= 0, consider the timestamp value invalid. - std::pair packData(CPacket& packet); + std::pair packData(CPacket& packet); int processData(CUnit* unit); void processClose(); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 62d158af7..1e57b3021 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -635,10 +635,10 @@ void* srt::CSndQueue::worker(void* param) // pack a packet from the socket CPacket pkt; - const std::pair res_time = u->packData((pkt)); + const std::pair res_time = u->packData((pkt)); // Check if payload size is invalid. - if (res_time.first <= 0) + if (res_time.first == false) { #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lNotReadyPop++; diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 2ba636525..edef007b6 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -465,7 +465,7 @@ TEST_F(CRcvBufferReadMsg, SmallReadBuffer) } // BUG!!! -// Checks signalling of read-readiness of a half-acknowledged message. +// Checks signaling of read-readiness of a half-acknowledged message. // The RCV buffer implementation has an issue here: when only half of the message is // acknowledged, the RCV buffer signals read-readiness, even though // the message can't be read, and reading returns 0. From 5773901bf2db9f05f2ecb4aa3557caa4558e25d8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 18:00:25 +0700 Subject: [PATCH 332/790] [core] Use SND buffer delay for TL Packet Drop instead of the timespan between the first and the last packet in the buffer. --- srtcore/buffer.cpp | 8 ++++++-- srtcore/buffer.h | 8 +++++--- srtcore/core.cpp | 25 +++++++++++-------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 4d95fe6ca..cbcbcf228 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -675,10 +675,14 @@ int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) return m_iCount; } -CSndBuffer::time_point CSndBuffer::getOldestTime() const +CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const { + ScopedLock lck(m_BufLock); SRT_ASSERT(m_pFirstBlock); - return m_pFirstBlock->m_tsOriginTime; + if (m_iCount == 0) + return duration(0); + + return tnow - m_pFirstBlock->m_tsOriginTime; } int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 6478e30b9..175ca19c0 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -179,7 +179,9 @@ class CSndBuffer int getAvgBufSize(int& bytes, int& timespan); int getCurrBufSize(int& bytes, int& timespan); - time_point getOldestTime() const; + /// @brief Get the buffering delay of the oldest message in the buffer. + /// @return the delay value. + duration getBufferingDelay(const time_point& tnow) const; uint64_t getInRatePeriod() const { return m_InRatePeriod; } @@ -207,7 +209,7 @@ class CSndBuffer static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - sync::Mutex m_BufLock; // used to synchronize buffer operation + mutable sync::Mutex m_BufLock; // used to synchronize buffer operation struct Block { @@ -216,7 +218,7 @@ class CSndBuffer int32_t m_iMsgNoBitset; // message number int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // block origin time (either provided from above or equials the time a message was submitted for sending. + time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending. time_point m_tsRexmitTime; // packet retransmission time int m_iTTL; // time to live (milliseconds) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 483836599..2aece2d3d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6338,9 +6338,8 @@ bool srt::CUDT::checkNeedDrop() throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); } - int bytes, timespan_ms; - // (returns buffer size in buffer units, ignored) - m_pSndBuffer->getCurrBufSize((bytes), (timespan_ms)); + const time_point tnow = steady_clock::now(); + const int buffdelay_ms = count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow)); // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms) // Minimum value must accomodate an I-Frame (~8 x average frame size) @@ -6348,21 +6347,19 @@ bool srt::CUDT::checkNeedDrop() // >>using 1 sec for worse case 1 frame using all bit budget. // picture rate would be useful in auto SRT setting for min latency // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable - int threshold_ms = 0; - if (m_config.iSndDropDelay >= 0) - { - threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + - (2 * COMM_SYN_INTERVAL_US / 1000); - } + const int threshold_ms = (m_config.iSndDropDelay >= 0) + ? std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + + (2 * COMM_SYN_INTERVAL_US / 1000) + : 0; bool bCongestion = false; - if (threshold_ms && timespan_ms > threshold_ms) + if (threshold_ms && buffdelay_ms > threshold_ms) { // protect packet retransmission enterCS(m_RecvAckLock); int dbytes; int32_t first_msgno; - int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), steady_clock::now() - milliseconds_from(threshold_ms)); + int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); if (dpkts > 0) { enterCS(m_StatsLock); @@ -6385,7 +6382,7 @@ bool srt::CUDT::checkNeedDrop() } HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" - << dpkts << "pkt " << dbytes << "B, span=" << timespan_ms << " ms, FIRST #" << first_msgno); + << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); #if ENABLE_EXPERIMENTAL_BONDING // This is done with a presumption that the group @@ -6413,10 +6410,10 @@ bool srt::CUDT::checkNeedDrop() bCongestion = true; leaveCS(m_RecvAckLock); } - else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2)) + else if (buffdelay_ms > (m_iPeerTsbPdDelay_ms / 2)) { HLOGC(aslog.Debug, - log << "cong, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms"); + log << "cong TIMESPAN " << buffdelay_ms << "ms"); bCongestion = true; } From 308cd309246745fca438b960fb6f6764f32fa1c4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 18:11:20 +0700 Subject: [PATCH 333/790] [core] Added missing lock to CSndBuffer::readData --- srtcore/buffer.cpp | 1 + srtcore/buffer.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index cbcbcf228..1cd983be2 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -402,6 +402,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int readlen = 0; w_seqnoinc = 0; + ScopedLock bufferguard(m_BufLock); while (m_pCurrBlock != m_pLastBlock) { // Make the packet REFLECT the data stored in the buffer. diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 175ca19c0..693858fe3 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -133,12 +133,14 @@ class CSndBuffer /// @param [in] data pointer to the user data block. /// @param [in] len size of the block. /// @param [inout] w_mctrl Message control data + SRT_ATTR_EXCLUDES(m_BufLock) void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); /// Read a block of data from file and insert it into the sending list. /// @param [in] ifs input file stream. /// @param [in] len size of the block. /// @return actual size of data added from the file. + SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. @@ -147,6 +149,7 @@ class CSndBuffer /// @param [in] kflags Odd|Even crypto key flag /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. /// @return Actual length of data read. + SRT_ATTR_EXCLUDES(m_BufLock) int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); /// Find data position to pack a DATA packet for a retransmission. @@ -155,12 +158,14 @@ class CSndBuffer /// @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). + SRT_ATTR_EXCLUDES(m_BufLock) int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// 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) /// /// @return Last time of the last retransmission event for the corresponding DATA packet. + SRT_ATTR_EXCLUDES(m_BufLock) time_point getPacketRexmitTime(const int offset); /// Update the ACK point and may release/unmap/return the user data according to the flag. @@ -173,6 +178,7 @@ class CSndBuffer /// @return Current size of the data in the sending list. int getCurrBufSize() const; + SRT_ATTR_EXCLUDES(m_BufLock) int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); void updAvgBufSize(const time_point& time); @@ -181,6 +187,7 @@ class CSndBuffer /// @brief Get the buffering delay of the oldest message in the buffer. /// @return the delay value. + SRT_ATTR_EXCLUDES(m_BufLock) duration getBufferingDelay(const time_point& tnow) const; uint64_t getInRatePeriod() const { return m_InRatePeriod; } From 912463b9111e9265e58490f22b5237d8cecdb38b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 24 Jan 2022 11:46:08 +0700 Subject: [PATCH 334/790] [core] Fix MaxBW limitation. Don't reschedule sending (keep pacing) on - SND drop, - NAK received - retransmission timeout. --- srtcore/congctl.cpp | 2 +- srtcore/core.cpp | 136 +++++++++++++++++++++----------------------- srtcore/core.h | 9 ++- srtcore/stats.h | 3 +- 4 files changed, 72 insertions(+), 78 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index b394f8183..33b5389e0 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -77,7 +77,7 @@ class LiveCC: public SrtCongestionControlBase { m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE m_zMaxPayloadSize = parent->OPT_PayloadSize(); - if ( m_zMaxPayloadSize == 0 ) + if (m_zMaxPayloadSize == 0) m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2aece2d3d..62da98d45 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5174,8 +5174,7 @@ void * srt::CUDT::tsbpd(void* param) rxready = true; if (info.seq_gap) { - const int iDropCnt SRT_ATR_UNUSED = self->dropTooLateUpTo(info.seqno); - + const int iDropCnt SRT_ATR_UNUSED = self->rcvDropTooLateUpTo(info.seqno); #if ENABLE_EXPERIMENTAL_BONDING shall_update_group = true; #endif @@ -5303,7 +5302,7 @@ void * srt::CUDT::tsbpd(void* param) return NULL; } -int srt::CUDT::dropTooLateUpTo(int seqno) +int srt::CUDT::rcvDropTooLateUpTo(int seqno) { const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); @@ -6327,10 +6326,10 @@ int srt::CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -bool srt::CUDT::checkNeedDrop() +int srt::CUDT::sndDropTooLate() { if (!m_bPeerTLPktDrop) - return false; + return 0; if (!m_config.bMessageAPI) { @@ -6352,72 +6351,64 @@ bool srt::CUDT::checkNeedDrop() + (2 * COMM_SYN_INTERVAL_US / 1000) : 0; - bool bCongestion = false; - if (threshold_ms && buffdelay_ms > threshold_ms) - { - // protect packet retransmission - enterCS(m_RecvAckLock); - int dbytes; - int32_t first_msgno; - int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); - if (dpkts > 0) - { - enterCS(m_StatsLock); - m_stats.sndr.dropped.count(stats::BytesPackets(dbytes, dpkts)); - leaveCS(m_StatsLock); + if (threshold_ms == 0 || buffdelay_ms <= threshold_ms) + return 0; - IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); - const int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts); + // protect packet retransmission + ScopedLock rcvlck(m_RecvAckLock); + int dbytes; + int32_t first_msgno; + const int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); + if (dpkts <= 0) + return 0; - m_iSndLastAck = fakeack; - m_iSndLastDataAck = fakeack; + // If some packets were dropped update stats, socket state, loss list and the parent group if any. + enterCS(m_StatsLock); + m_stats.sndr.dropped.count(dbytes);; + leaveCS(m_StatsLock); - int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck); - m_pSndLossList->removeUpTo(minlastack); - /* If we dropped packets not yet sent, advance current position */ - // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1) - if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0) - { - m_iSndCurrSeqNo = minlastack; - } + IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); + const int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts); - HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" - << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); + m_iSndLastAck = fakeack; + m_iSndLastDataAck = fakeack; -#if ENABLE_EXPERIMENTAL_BONDING - // This is done with a presumption that the group - // exists and if this is not NULL, it means that this - // function was called with locked m_GroupLock, as sendmsg2 - // function was called from inside CUDTGroup::send, which - // locks the whole function. - // - // XXX This is true only because all existing groups are managed - // groups, that is, sockets cannot be added or removed from group - // manually, nor can send/recv operation be done on a single socket - // from the API call directly. This should be extra verified, if that - // changes in the future. - // - if (m_parent->m_GroupOf) - { - // What's important is that the lock on GroupLock cannot be applied - // here, both because it might be applied already, that is, according - // to the condition defined at this function's header, it is applied - // under this condition. Hence ackMessage can be defined as 100% locked. - m_parent->m_GroupOf->ackMessage(first_msgno); - } -#endif - } - bCongestion = true; - leaveCS(m_RecvAckLock); - } - else if (buffdelay_ms > (m_iPeerTsbPdDelay_ms / 2)) + const int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck); + m_pSndLossList->removeUpTo(minlastack); + /* If we dropped packets not yet sent, advance current position */ + // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1) + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0) { - HLOGC(aslog.Debug, - log << "cong TIMESPAN " << buffdelay_ms << "ms"); + m_iSndCurrSeqNo = minlastack; + } - bCongestion = true; + HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" + << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); + +#if ENABLE_EXPERIMENTAL_BONDING + // This is done with a presumption that the group + // exists and if this is not NULL, it means that this + // function was called with locked m_GroupLock, as sendmsg2 + // function was called from inside CUDTGroup::send, which + // locks the whole function. + // + // XXX This is true only because all existing groups are managed + // groups, that is, sockets cannot be added or removed from group + // manually, nor can send/recv operation be done on a single socket + // from the API call directly. This should be extra verified, if that + // changes in the future. + // + if (m_parent->m_GroupOf) + { + // What's important is that the lock on GroupLock cannot be applied + // here, both because it might be applied already, that is, according + // to the condition defined at this function's header, it is applied + // under this condition. Hence ackMessage can be defined as 100% locked. + m_parent->m_GroupOf->ackMessage(first_msgno); } - return bCongestion; +#endif + + return dpkts; } int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) @@ -6520,9 +6511,9 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) m_iReXmitCount = 1; } - // checkNeedDrop(...) may lock m_RecvAckLock + // sndDropTooLate(...) may lock m_RecvAckLock // to modify m_pSndBuffer and m_pSndLossList - const bool bCongestion = checkNeedDrop(); + const int iPktsTLDropped SRT_ATR_UNUSED = sndDropTooLate(); int minlen = 1; // Minimum sender buffer space required for STREAM API if (m_config.bMessageAPI) @@ -6701,12 +6692,13 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) } } - // insert this socket to the snd list if it is not on the list yet + // Insert this socket to the snd list if it is not on the list already. // m_pSndUList->pop may lock CSndUList::m_ListLock and then m_RecvAckLock - m_pSndQueue->m_pSndUList->update(this, CSndUList::rescheduleIf(bCongestion)); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); #ifdef SRT_ENABLE_ECN - if (bCongestion) + // IF there was a packet drop on the sender side, report congestion to the app. + if (iPktsTLDropped > 0) { LOGC(aslog.Error, log << "sendmsg2: CONGESTION; reporting error"); throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0); @@ -8192,7 +8184,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // Guard access to m_iSndAckedMsgNo field // Note: This can't be done inside CUDTGroup::ackMessage - // because this function is also called from CUDT::checkNeedDrop + // because this function is also called from CUDT::sndDropTooLate // called from CUDT::sendmsg2 called from CUDTGroup::send, which // applies the lock on m_GroupLock already. ScopedLock glk (*m_parent->m_GroupOf->exp_groupLock()); @@ -8696,7 +8688,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) } // the lost packet (retransmission) should be sent out immediately - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); enterCS(m_StatsLock); m_stats.sndr.recvdNak.count(1); @@ -11166,8 +11158,8 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT; updateCC(TEV_CHECKTIMER, EventVariant(stage)); - // immediately restart transmission - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + // schedule sending if not scheduled already + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); } void srt::CUDT::checkTimers() diff --git a/srtcore/core.h b/srtcore/core.h index c25e9c8c1..caf1f9837 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -543,7 +543,10 @@ class CUDT void updateIdleLinkFrom(CUDT* source); - bool checkNeedDrop(); + /// @brief Drop packets too late to be delivered if any. + /// @returns the number of packets actually dropped. + SRT_ATTR_REQUIRES(m_RecvAckLock, m_StatsLock) + int sndDropTooLate(); /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, @@ -706,11 +709,11 @@ class CUDT static void* tsbpd(void* param); #if ENABLE_NEW_RCVBUFFER - /// Drop too late packets. Updaet loss lists and ACK positions. + /// Drop too late packets (receiver side). Updaet 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. /// @return The number of packets dropped. - int dropTooLateUpTo(int seqno); + int rcvDropTooLateUpTo(int seqno); #endif void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); diff --git a/srtcore/stats.h b/srtcore/stats.h index 6d934b0a7..10523f851 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -91,8 +91,7 @@ class BytesPackets uint64_t bytesWithHdr() const { - static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - return m_bytes + m_packets * PKT_HDR_SIZE; + return m_bytes + m_packets * CPacket::SRT_DATA_HDR_SIZE; } private: From ef11d26cd0c8b0cc139960e8669d77817a6d7279 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 25 Jan 2022 15:45:42 +0700 Subject: [PATCH 335/790] [core] SND pacing: amendment on probing packets --- srtcore/core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 62da98d45..d893c671d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9313,10 +9313,13 @@ std::pair srt::CUDT::packData(CPacket& w_packet) m_stats.sndr.sentUnique.count(payload); leaveCS(m_StatsLock); + const duration sendint = m_tdSendInterval; if (probe) { // sends out probing packet pair m_tsNextSendTime = enter_time; + // Sending earlier, need to adjust the pace later on. + m_tdSendTimeDiff = m_tdSendTimeDiff.load() - sendint; probe = false; } else @@ -9324,7 +9327,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if USE_BUSY_WAITING m_tsNextSendTime = enter_time + m_tdSendInterval.load(); #else - const duration sendint = m_tdSendInterval; const duration sendbrw = m_tdSendTimeDiff; if (sendbrw >= sendint) From f372356bd5ed124a6f02e1d202c2deca99fe3eef Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 27 Jan 2022 10:37:12 +0100 Subject: [PATCH 336/790] [tests] Use std::random_device in Transmission.FileUpload --- test/test_file_transmission.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 8150ca3f2..5a646fb7d 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -18,9 +18,11 @@ #include "srt.h" +#include #include #include #include +#include #include //#pragma comment (lib, "ws2_32.lib") @@ -35,7 +37,7 @@ TEST(Transmission, FileUpload) SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); - int tt = SRTT_FILE; + const int tt = SRTT_FILE; srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); @@ -75,11 +77,13 @@ TEST(Transmission, FileUpload) std::ofstream outfile("file.source", std::ios::out | std::ios::binary); ASSERT_EQ(!!outfile, true) << srt_getlasterror_str(); - srand(time(0)); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); for (size_t i = 0; i < filesize; ++i) { - char outbyte = rand() % 255; + char outbyte = dis(mtrd); outfile.write(&outbyte, 1); } } From 08e6482fe6814d6c4a6929e101ed9874f08593f6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 27 Jan 2022 16:06:19 +0700 Subject: [PATCH 337/790] [core] SND prioritize original packets in live configuration --- srtcore/buffer.cpp | 9 +++ srtcore/buffer.h | 5 ++ srtcore/core.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++- srtcore/core.h | 6 ++ 4 files changed, 155 insertions(+), 1 deletion(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 1cd983be2..febd12f58 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -455,6 +455,15 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, return readlen; } +CSndBuffer::time_point CSndBuffer::peekNextOriginal() const +{ + ScopedLock bufferguard(m_BufLock); + if (m_pCurrBlock == m_pLastBlock) + return time_point(); + + return m_pCurrBlock->m_tsOriginTime; +} + int32_t CSndBuffer::getMsgNoAt(const int offset) { ScopedLock bufferguard(m_BufLock); diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 693858fe3..80a2354ff 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -152,6 +152,11 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); + /// Peek an information on the next original data packet to send. + /// @return origin time stamp of the next packet; epoch start time otherwise. + SRT_ATTR_EXCLUDES(m_BufLock) + time_point peekNextOriginal() const; + /// Find data position to pack a DATA packet for a retransmission. /// @param [in] offset offset from the last ACK point (backward sequence number difference) /// @param [out] packet the packet to read. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d893c671d..6f228c138 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9178,6 +9178,131 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi return 0; } +#if SRT_DEBUG_TRACE_SND +class snd_logger +{ + typedef srt::sync::steady_clock steady_clock; + +public: + snd_logger() {} + + ~snd_logger() + { + ScopedLock lck(m_mtx); + m_fout.close(); + } + + struct + { + typedef srt::sync::steady_clock steady_clock; + long long usElapsed; + steady_clock::time_point tsNow; + int usSRTT; + int usRTTVar; + int msSndBuffSpan; + int msTimespanTh; + int msNextUniqueToSend; + long long usElapsedLastDrop; + bool canRexmit; + int iPktSeqno; + bool isRetransmitted; + } state; + + void trace() + { + using namespace srt::sync; + ScopedLock lck(m_mtx); + create_file(); + + m_fout << state.usElapsed << ","; + m_fout << state.usSRTT << ","; + m_fout << state.usRTTVar << ","; + m_fout << state.msSndBuffSpan << ","; + m_fout << state.msTimespanTh << ","; + m_fout << state.msNextUniqueToSend << ","; + m_fout << state.usElapsedLastDrop << ","; + m_fout << state.canRexmit << ","; + m_fout << state.iPktSeqno << ','; + m_fout << state.isRetransmitted << '\n'; + + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "usElapsed,usSRTT,usRTTVar,msSndBuffTimespan,msTimespanTh,msNextUniqueToSend,usDLastDrop,canRexmit,sndPktSeqno,isRexmit"; + m_fout << "\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + m_start_time = srt::sync::steady_clock::now(); + std::string str_tnow = srt::sync::FormatTimeSys(m_start_time); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) + { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "snd_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; + srt::sync::steady_clock::time_point m_start_time; +}; + +snd_logger g_snd_logger; +#endif // SRT_DEBUG_TRACE_SND + +bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) +{ + // Prioritization of original packets only applies to Live CC. + if (!m_bPeerTLPktDrop || !m_config.bMessageAPI) + return true; + + // TODO: lock sender buffer? + const time_point tsNextPacket = m_pSndBuffer->peekNextOriginal(); + +#if SRT_DEBUG_TRACE_SND + const int buffdelay_ms = count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow)); + // If there is a small loss, still better to retransmit. If timespan is already big, + // then consider sending original packets. + const int threshold_ms_min = (2 * m_iSRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) / 1000; + const int msNextUniqueToSend = count_milliseconds(tnow - tsNextPacket) + m_iPeerTsbPdDelay_ms; + + g_snd_logger.state.tsNow = tnow; + g_snd_logger.state.usElapsed = count_microseconds(tnow - m_stats.tsStartTime); + g_snd_logger.state.usSRTT = m_iSRTT; + g_snd_logger.state.usRTTVar = m_iRTTVar; + g_snd_logger.state.msSndBuffSpan = buffdelay_ms; + g_snd_logger.state.msTimespanTh = threshold_ms_min; + g_snd_logger.state.msNextUniqueToSend = msNextUniqueToSend; + g_snd_logger.state.usElapsedLastDrop = count_microseconds(tnow - m_tsLastTLDrop); + g_snd_logger.state.canRexmit = false; +#endif + + if (tsNextPacket != time_point()) + { + // Can send original packet, so just send it + return false; + } + +#if SRT_DEBUG_TRACE_SND + g_snd_logger.state.canRexmit = true; +#endif + return true; +} + std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; @@ -9206,7 +9331,10 @@ std::pair srt::CUDT::packData(CPacket& w_packet) if (!m_bOpened) return std::make_pair(false, enter_time); - payload = packLostData((w_packet), (origintime)); + payload = isRetransmissionAllowed(enter_time) + ? packLostData((w_packet), (origintime)) + : 0; + if (payload > 0) { reason = "reXmit"; @@ -9459,6 +9587,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) } } +#if SRT_DEBUG_TRACE_SND + g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); + g_snd_logger.trace(); +#endif + return true; } diff --git a/srtcore/core.h b/srtcore/core.h index caf1f9837..c660f6732 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -548,6 +548,12 @@ class CUDT SRT_ATTR_REQUIRES(m_RecvAckLock, m_StatsLock) int sndDropTooLate(); + /// @bried Allow packet retransmission. + /// Depending on the configuration mode (live / file), retransmission + /// can be blocked if e.g. there are original packets pending to be sent. + /// @return true if retransmission is allowed; false otherwise. + bool isRetransmissionAllowed(const time_point& tnow); + /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, /// and then send the response back to the caller. From 409d3635d02925b66e03fc78310f1cc746778327 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Tue, 1 Feb 2022 15:49:36 +0700 Subject: [PATCH 338/790] [core] Improved the condition for smoothed_rtt recalculation, bidirectional transmission --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6f228c138..afcb65126 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8382,7 +8382,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // TODO: The case of bidirectional transmission requires further // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + if (rtt != INITIAL_RTT || rttvar != INITIAL_RTTVAR) { int iSRTT = m_iSRTT.load(), iRTTVar = m_iRTTVar.load(); iRTTVar = avg_iir<4>(iRTTVar, abs(rtt - iSRTT)); From 5b7ac45de5415ded11d493409368a936a0a169b2 Mon Sep 17 00:00:00 2001 From: Alain Kalker Date: Mon, 7 Feb 2022 09:20:34 +0100 Subject: [PATCH 339/790] [docs] srt-live-transmit.md: add ffplay, ffprobe examples (#2242) --- docs/apps/srt-live-transmit.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 258a5bb02..e0c86bb9d 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -62,6 +62,16 @@ You should see the stream connect in `srt-live-transmit`. Now you can test in VLC (make sure you're using the latest version!) - just go to file -> open network stream and enter `srt://127.0.0.1:4201` and you should see bars and tone right away. +Or you can test using ffplay or ffprobe to inspect the stream: + +```shell +ffplay srt://127.0.0.1:4201 +``` +-or- +```shell +ffprobe srt://127.0.0.1:4201 +``` + If you're having trouble, make sure this works, then add complexity one step at a time (multicast, push vs listen, etc.). ## URI Syntax From ac854f262ac2a78f152005876cec0da593845166 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 20 Jan 2022 09:56:13 +0700 Subject: [PATCH 340/790] [core] Fixed setting the peer rexmit flag on the RCV buffer --- srtcore/buffer_rcv.cpp | 4 ++-- srtcore/buffer_rcv.h | 6 ++++-- srtcore/core.cpp | 20 ++++++++++---------- srtcore/core.h | 1 + test/test_buffer.cpp | 3 ++- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7e6e5a830..dd32f518c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -70,7 +70,7 @@ namespace { * m_iMaxPosInc: none? (modified on add and ack */ -CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI) +CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) @@ -81,7 +81,7 @@ CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, , m_iNotch(0) , m_numOutOfOrderPackets(0) , m_iFirstReadableOutOfOrder(-1) - , m_bPeerRexmitFlag(peerRexmit) + , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index f7e01d930..0ad8d4245 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -51,7 +51,7 @@ class CRcvBufferNew typedef sync::steady_clock::duration duration; public: - CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI); + CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI); ~CRcvBufferNew(); @@ -308,7 +308,7 @@ class CRcvBufferNew size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to // read - const bool m_bPeerRexmitFlag; // Needed to read message number correctly + bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. public: // TSBPD public functions @@ -320,6 +320,8 @@ class CRcvBufferNew /// @return 0 void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + void setPeerRexmitFlag(bool flag) { m_bPeerRexmitFlag = flag; } + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index afcb65126..8c696cd4e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2414,7 +2414,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } // We still believe it should work, let's check the flags. - int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType); + const int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType); if (ext_flags == 0) { m_RejectReason = SRT_REJ_ROGUE; @@ -4019,15 +4019,16 @@ EConnectStatus srt::CUDT::processRendezvous( m_ConnReq.m_iReqType = rsp_type; m_ConnReq.m_extension = needs_extension; - // This must be done before prepareConnectionObjects(). + // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers. if (!applyResponseSettings()) { LOGC(cnlog.Error, log << "processRendezvous: rogue peer"); return CONN_REJECT; } - // This must be done before interpreting and creating HSv5 extensions. - if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0)) + // The CryptoControl must be created by the prepareConnectionObjects() before interpreting and creating HSv5 extensions + // because the it will be used there. + if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, NULL)) { // m_RejectReason already handled HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in prepareConnectionObjects."); @@ -4536,6 +4537,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // however in this case the HSREQ extension will not be attached, // so it will simply go the "old way". // (&&: skip if failed already) + // Must be called before interpretSrtHandshake() to create the CryptoControl. ok = ok && prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout); // May happen that 'response' contains a data packet that was sent in rendezvous mode. @@ -5568,11 +5570,8 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd return true; } - bool bidirectional = false; - if (hs.m_iVersion > HS_VERSION_UDT4) - { - bidirectional = true; // HSv5 is always bidirectional - } + // HSv5 is always bidirectional + const bool bidirectional = (hs.m_iVersion > HS_VERSION_UDT4); // HSD_DRAW is received only if this side is listener. // If this side is caller with HSv5, HSD_INITIATOR should be passed. @@ -5595,7 +5594,7 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); #if ENABLE_NEW_RCVBUFFER SRT_ASSERT(m_iISN != -1); - m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_bPeerRexmitFlag, m_config.bMessageAPI); + m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_config.bMessageAPI); #else m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.iRcvBufSize); #endif @@ -8982,6 +8981,7 @@ void srt::CUDT::updateSrtRcvSettings() enterCS(m_RecvLock); #if ENABLE_NEW_RCVBUFFER m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms)); + m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag); #else m_pRcvBuffer->setRcvTsbPdMode(m_tsRcvPeerStartTime, milliseconds_from(m_iTsbPdDelay_ms)); #endif diff --git a/srtcore/core.h b/srtcore/core.h index c660f6732..62027b026 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -489,6 +489,7 @@ class CUDT SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + /// Create the CryptoControl object based on the HS packet. Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index edef007b6..287297a00 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -34,7 +34,8 @@ class CRcvBufferReadMsg #if ENABLE_NEW_RCVBUFFER const bool enable_msg_api = m_use_message_api; const bool enable_peer_rexmit = true; - m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); + m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_msg_api)); + m_rcv_buffer->setPeerRexmitFlag(enable_peer_rexmit); #else m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); #endif From 8f68f613c5546aca16b301ab0dceae6db28bb6c3 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 8 Dec 2021 21:08:39 +0800 Subject: [PATCH 341/790] [core] refactor Group::recv() base on new rcv buffer to support message mode --- srtcore/buffer_rcv.cpp | 47 +++++++++-- srtcore/buffer_rcv.h | 2 + srtcore/group.cpp | 188 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index dd32f518c..8ac7e7a81 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -292,8 +292,6 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; - // Remember if we actually read out of order packet. - const bool readingOutOfOrderPacket = !canReadInOrder || m_iStartPos == m_iFirstReadableOutOfOrder; size_t remain = len; char* dst = data; @@ -331,13 +329,14 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) { - msgctrl->pktseq = pktseqno; msgctrl->msgno = packet.getMsgSeq(m_bPeerRexmitFlag); } if (msgctrl && pbLast) { msgctrl->srctime = count_microseconds(getPktTsbPdTime(packet.getMsgTimeStamp()).time_since_epoch()); } + if (msgctrl) + msgctrl->pktseq = pktseqno; releaseUnitInPos(i); if (updateStartPos) @@ -362,8 +361,6 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) } countBytes(-pkts_read, -bytes_extracted); - if (!m_tsbpd.isEnabled() && readingOutOfOrderPacket) - updateFirstReadableOutOfOrder(); releaseNextFillerEntries(); @@ -373,6 +370,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) //updateNonreadPos(); } + if (!m_tsbpd.isEnabled()) + // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // incase readable inorder packets are all read out. + updateFirstReadableOutOfOrder(); + const int bytes_read = dst - data; if (bytes_read < bytes_extracted) { @@ -606,6 +608,41 @@ bool CRcvBufferNew::isRcvDataReady(time_point time_now) const return info.tsbpd_time <= time_now; } +CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point time_now) const +{ + const PacketInfo unreadableInfo = {SRT_SEQNO_NONE, false, time_point()}; + const bool hasInorderPackets = hasReadableInorderPkts(); + + if (!m_tsbpd.isEnabled()) + { + if (hasInorderPackets) + { + const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const PacketInfo info = {packet.getSeqNo(), false, time_point()}; + return info; + } + SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstReadableOutOfOrder >= 0) + { + SRT_ASSERT(m_numOutOfOrderPackets > 0); + const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + const PacketInfo info = {packet.getSeqNo(), true, time_point()}; + return info; + } + return unreadableInfo; + } + + if (!hasInorderPackets) + return unreadableInfo; + + const PacketInfo info = getFirstValidPacketInfo(); + + if (info.tsbpd_time <= time_now) + return info; + else + return unreadableInfo; +} + void CRcvBufferNew::countBytes(int pkts, int bytes) { ScopedLock lock(m_BytesCountLock); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 0ad8d4245..c21b037c3 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -164,6 +164,8 @@ class CRcvBufferNew /// IF skipseqno == -1, no missing packet but 1st not ready to play. PacketInfo getFirstValidPacketInfo() const; + PacketInfo getFirstReadablePacketInfo(time_point time_now) const; + /// Get information on packets available to be read. /// @returns a pair of sequence numbers (first available; first unavailable). /// diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b9c18090a..c1ba15d75 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2195,6 +2195,193 @@ static bool isValidSeqno(int32_t iBaseSeqno, int32_t iPktSeqno) return false; } +#ifdef ENABLE_NEW_RCVBUFFER +int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) +{ + // First, acquire GlobControlLock to make sure all member sockets still exist + enterCS(m_Global.m_GlobControlLock); + ScopedLock guard(m_GroupLock); + + if (m_bClosing) + { + // The group could be set closing in the meantime, but if + // this is only about to be set by another thread, this thread + // must fist wait for being able to acquire this lock. + // The group will not be deleted now because it is added usage counter + // by this call, but will be released once it exits. + leaveCS(m_Global.m_GlobControlLock); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + // Now, still under lock, check if all sockets still can be dispatched + send_CheckValidSockets(); + leaveCS(m_Global.m_GlobControlLock); + + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + // Later iteration over it might be less efficient than + // by vector, but we'll also often try to check a single id + // if it was ever seen broken, so that it's skipped. + set broken; + + for (;;) + { + if (!m_bOpened || !m_bConnected) + { + LOGC(grlog.Error, + log << boolalpha << "grp/recv: $" << id() << ": ABANDONING: opened=" << m_bOpened + << " connected=" << m_bConnected); + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } + + vector aliveMembers; + recv_CollectAliveAndBroken(aliveMembers, broken); + if (aliveMembers.empty()) + { + LOGC(grlog.Error, log << "grp/recv: ALL LINKS BROKEN, ABANDONING."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } + + vector readySockets; + if (m_bSynRecving) + readySockets = recv_WaitForReadReady(aliveMembers, broken); + else + readySockets = aliveMembers; + + if (m_bClosing) + { + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": GROUP CLOSED, ABANDONING."); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + // Find the first readable packet among all member sockets. + CUDTSocket* socketToRead = NULL; + CRcvBufferNew::PacketInfo infoToRead = {-1, false, time_point()}; + for (vector::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) + { + CUDTSocket* ps = *si; + + ScopedLock lg(ps->core().m_RcvBufferLock); + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + // Drop here to make sure the getFirstReadablePacketInfo() below return fresher packet. + int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + if (cnt > 0) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": dropped " << cnt + << " packets before reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo); + } + } + + const CRcvBufferNew::PacketInfo info = + ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": Nothing to read."); + continue; + } + // We need to qualify the sequence, just for a case. + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, info.seqno)) + { + LOGC(grlog.Error, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" + << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + ps->core().m_bBroken = true; + broken.insert(ps); + continue; + } + if (socketToRead == NULL || CSeqNo::seqcmp(info.seqno, infoToRead.seqno) < 0) + { + socketToRead = ps; + infoToRead = info; + } + } + + if (socketToRead == NULL) + { + if (m_bSynRecving) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": No links reported any fresher packet, re-polling."); + continue; + } + else + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": No links reported any fresher packet, clearing readiness."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + } + else + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": Found first readable packet from @" << socketToRead->m_SocketID + << ": seq=" << infoToRead.seqno << " gap=" << infoToRead.seq_gap + << " time=" << FormatTime(infoToRead.tsbpd_time)); + } + + const int res = socketToRead->core().receiveMessage((buf), len, (w_mc), CUDTUnited::ERH_RETURN); + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": Extracted data with %" + << w_mc.pktseq << " #" << w_mc.msgno << ": " << (res <= 0 ? "(NOTHING)" : BufferStamp(buf, res))); + if (res == 0) + { + LOGC(grlog.Warn, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": Retrying next socket..."); + // This socket will not be socketToRead in the next turn because receiveMessage() return 0 here. + continue; + } + if (res == SRT_ERROR) + { + LOGC(grlog.Warn, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": " << srt_getlasterror_str() + << ". Retrying next socket..."); + broken.insert(socketToRead); + continue; + } + fillGroupData((w_mc), w_mc); + + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": Update m_RcvBaseSeqNo: %" << m_RcvBaseSeqNo << " -> %" << w_mc.pktseq); + m_RcvBaseSeqNo = w_mc.pktseq; + + // Update stats as per delivery + m_stats.recv.count(res); + updateAvgPayloadSize(res); + + for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) + { + CUDTSocket* ps = *si; + ScopedLock lg(ps->core().m_RcvBufferLock); + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + if (cnt > 0) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": dropped " << cnt + << " packets after reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo); + } + } + } + for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) + { + CUDTSocket* ps = *si; + if (!ps->core().isRcvBufferReady()) + m_Global.m_EPoll.update_events(ps->m_SocketID, ps->core().m_sPollID, SRT_EPOLL_IN, false); + } + + return res; + } + LOGC(grlog.Error, log << "grp/recv: UNEXPECTED RUN PATH, ABANDONING."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); +} +#else // The "app reader" version of the reading function. // This reads the packets from every socket treating them as independent // and prepared to work with the application. Then packets are sorted out @@ -2731,6 +2918,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } } } +#endif // [[using locked(m_GroupLock)]] CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead() From 650dbe6d8231f3ffc14c16d9c08b4f51f67cac78 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 7 Feb 2022 15:11:54 +0100 Subject: [PATCH 342/790] [core] Fixed rcvDropTooLateUpTo calls. Broken after merging #2218 --- srtcore/group.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c1ba15d75..66a78321d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2267,7 +2267,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { // Drop here to make sure the getFirstReadablePacketInfo() below return fresher packet. - int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); if (cnt > 0) { HLOGC(grlog.Debug, @@ -2359,7 +2359,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); if (cnt > 0) { HLOGC(grlog.Debug, From c885ed152d8222800bd498167b90031de5878228 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Thu, 6 Jan 2022 14:16:44 +0800 Subject: [PATCH 343/790] [core] Group::updateReadState() support out-of-order messages --- srtcore/core.cpp | 27 +++++++++++++-------------- srtcore/core.h | 3 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 8c696cd4e..29fe71fd0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7663,7 +7663,7 @@ void srt::CUDT::releaseSynch() } // [[using locked(m_RcvBufferLock)]]; -int32_t srt::CUDT::ackDataUpTo(int32_t ack) +void srt::CUDT::ackDataUpTo(int32_t ack) { const int acksize SRT_ATR_UNUSED = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); @@ -7673,16 +7673,7 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) m_iRcvLastAck = ack; m_iRcvLastSkipAck = ack; -#if ENABLE_NEW_RCVBUFFER - const std::pair range = m_pRcvBuffer->getAvailablePacketsRange(); - // Some packets acknowledged are not available in the buffer. - if (CSeqNo::seqcmp(range.second, ack) < 0) - { - LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first - << " - %" << range.second); - } - return CSeqNo::decseq(range.second); -#else +#if !ENABLE_NEW_RCVBUFFER // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. @@ -7690,7 +7681,6 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) { m_pRcvBuffer->ackData(acksize); } - return ack; #endif } @@ -7930,7 +7920,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t group_read_seq SRT_ATR_UNUSED = ackDataUpTo(ack); + ackDataUpTo(ack); + +#if ENABLE_EXPERIMENTAL_BONDING +#if ENABLE_NEW_RCVBUFFER + const int32_t group_read_seq = m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()).seqno; +#else + const int32_t group_read_seq = CSeqNo::decseq(ack); +#endif +#endif + InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8015,7 +8014,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_GroupOf) + if (group_read_seq != SRT_SEQNO_NONE && m_parent->m_GroupOf) { // See above explanation for double-checking ScopedLock glock (uglobal().m_GlobControlLock); diff --git a/srtcore/core.h b/srtcore/core.h index 62027b026..4f87ad181 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1075,8 +1075,7 @@ class CUDT /// @brief Acknowledge reading position up to the @p seq. /// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq. /// @param seq first unacknowledged packet sequence number. - /// @return - int32_t ackDataUpTo(int32_t seq); + void ackDataUpTo(int32_t seq); void handleKeepalive(const char* data, size_t lenghth); From 0c5bf7a8e99a59b1e8fd5bebb888beaf9be19900 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 14 Feb 2022 15:21:39 +0100 Subject: [PATCH 344/790] [core] Decreased SND drop request log level to Debug --- srtcore/buffer_rcv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 8ac7e7a81..ce9437b51 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -244,8 +244,8 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); if (offset_b < 0) { - LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " + << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); return; } From 81a31da7d378e5ffcfabc8058ebefd1ee4337ecb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 18 Feb 2022 10:55:05 +0100 Subject: [PATCH 345/790] [core] Fix RCV drop count when dropping on SND DROP REQ. Extended RCVBUF trace logging. --- srtcore/buffer_rcv.cpp | 26 +++++++++++++++++++------- srtcore/buffer_rcv.h | 3 ++- srtcore/core.cpp | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ce9437b51..c05eb4e77 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -110,7 +110,9 @@ int CRcvBufferNew::insert(CUnit* unit) const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno); + IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); + IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); if (offset < 0) { @@ -198,7 +200,7 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) return iDropCnt; } -void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) +int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); @@ -206,7 +208,9 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno != 0) { + IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; + int iDropCnt = 0; for (int i = m_iStartPos; i != end_pos; i = incPos(i)) { // TODO: Maybe check status? @@ -216,12 +220,14 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { + ++iDropCnt; dropUnitInPos(i); m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); } } + IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); // Check if units before m_iFirstNonreadPos are dropped. bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); @@ -236,7 +242,7 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) m_iFirstReadableOutOfOrder = -1; updateFirstReadableOutOfOrder(); } - return; + return iDropCnt; } // Drop by packet seqno range. @@ -246,15 +252,17 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); - return; + return 0; } const int start_off = max(0, offset_a); const int last_pos = incPos(m_iStartPos, offset_b); int minDroppedOffset = -1; + int iDropCnt = 0; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { dropUnitInPos(i); + ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); @@ -277,6 +285,8 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) m_iFirstReadableOutOfOrder = -1; updateFirstReadableOutOfOrder(); } + + return iDropCnt; } int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) @@ -288,11 +298,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) return 0; } - IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); + size_t remain = len; char* dst = data; int pkts_read = 0; @@ -381,6 +391,8 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) LOGC(rbuflog.Error, log << "readMessage: small dst buffer, copied only " << bytes_read << "/" << bytes_extracted << " bytes."); } + IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + return bytes_read; } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index c21b037c3..a5e113dfc 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -79,7 +79,8 @@ class CRcvBufferNew /// @param seqnolo sequence number of the first packet in the dropping range. /// @param seqnohi sequence number of the last packet in the dropping range. /// @param msgno message number to drop (0 if unknown) - void dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + /// @return the number of packets actually dropped. + int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); /// Read the whole message from one or several packets. /// diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 29fe71fd0..de4e6b548 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8814,7 +8814,20 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER - m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + + if (iDropCnt > 0) + { + LOGC(brlog.Warn, log << CONID() << "RCV-DROPPED " << iDropCnt << " packet(s), seqno range %" + << dropdata[0] << "-%" << dropdata[1] << ", msgno " << ctrlpkt.getMsgSeq(using_rexmit_flag) + << " (SND DROP REQUEST)."); + + 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, (size_t)iDropCnt)); + leaveCS(m_StatsLock); + } #else m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); #endif From 5adc2db2da20eff7620100874444a28c5f656db0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 18 Feb 2022 11:00:20 +0100 Subject: [PATCH 346/790] [core] RCV don't drop packets on SND drop request if they already exist in the buffer and can be read (full message is available). --- srtcore/buffer_rcv.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index c05eb4e77..5fb4a5992 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -217,6 +217,7 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) if (!m_entries[i].pUnit) continue; + // TODO: Break the loop if a massege has been found. No need to search further. const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { @@ -261,6 +262,11 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) int iDropCnt = 0; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { + // Don't drop messages, if all its packets are already in the buffer. + // TODO: Don't drop a several-packet message if all packets are in the buffer. + if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + continue; + dropUnitInPos(i); ++iDropCnt; m_entries[i].status = EntryState_Drop; From 7d77d417c24c3233340a94da1613512d19ba4194 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Feb 2022 09:59:44 +0100 Subject: [PATCH 347/790] [core] SND Drop Request: ignore if TLPktDrop and TSBPD are enabled to reduce false drops when a packet can still arrive later. It will be dropped anyway as too late. --- srtcore/core.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index de4e6b548..c8b2ed8c6 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8810,8 +8810,13 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { UniqueLock rlock(m_RecvLock); - const bool using_rexmit_flag = m_bPeerRexmitFlag; + // With both TLPktDrop and TsbPd enabled, a message always consists only of one packet. + // It will be dropped as too late anyway. Not dropping it from the receiver buffer + // in advance reduces false drops if the packet somehow manages to arrive. + // Still remove the record from the loss list to cease further retransmission requests. + if (!m_bTLPktDrop || !m_bTsbPd) { + const bool using_rexmit_flag = m_bPeerRexmitFlag; ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); @@ -8845,10 +8850,8 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) dropFromLossLists(dropdata[0], dropdata[1]); - // move forward with current recv seq no. - // SYMBOLIC: - // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo - // && dropdata[1] >% m_iRcvCurrSeqNo ) + // If dropping ahead of the current largest sequence number, + // move the recv seq number forward. if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) { From 9b95ffc383f18a8d55aacf16038fa8638b0061ca Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 2 Mar 2022 15:00:33 +0100 Subject: [PATCH 348/790] [docs] Reworked the build options document. (#2247) * [docs] Reworked the build options document. Describe CMake build options of SRT, reference corresponding option of the configure script. Co-authored-by: stevomatthews --- docs/build/build-options.md | 523 +++++++++++++++++++++++++----------- 1 file changed, 367 insertions(+), 156 deletions(-) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index cdac4c068..325c05821 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -1,44 +1,149 @@ # Build System -The main build system for SRT is provided by the `cmake` tool, which can be -used directly. There's also a wrapper script named `configure` (requires Tcl -interpreter) that can make operating with the options easier. +The SRT build system uses [`CMake`](https://cmake.org/) 2.8.12 or above. + +A wrapper script named [`configure`](https://github.com/Haivision/srt/blob/master/configure) +is also available. The `configure` script can simplify the build process, such as +by trying to automatically detect the OpenSSL path in a system. Note that you must +have the Tcl interpreter installed to use this script. -## Portability +Here is a link to a demo showing how CMake can be used to build SRT: +[Quickstart: Running SRT and FFmpeg on Ubuntu](https://www.youtube.com/watch?v=XOtUOVhussc&t=5s) -The `cmake` build system was tested on the following platforms: - - Linux (various flavors) - - macOS (see [Building SRT for iOS](build-iOS.md)) - - Windows with MinGW - - Windows with Microsoft Visual Studio (see [Building SRT for Windows](build-win.md)) - - Android (see [Building SRT for Android](build-android.md)) - - Cygwin (only for testing) +Additional information on building with Windows is available in the +[Building SRT for Windows](https://github.com/Haivision/srt/blob/master/docs/build/build-win.md) +document and on the [SRT Cookbook web site](https://srtlab.github.io/srt-cookbook/getting-started/build-on-windows/). -The `configure` script wasn't tested on Windows (other than on Cygwin). +## List of Build Options -## The `configure` Script +The following table lists available build options in alphabetical order. +Option details are given further below. -This script is similar in design to the Autotools' `configure` script, and -so usually handles `--long-options`, possibly with values. It handles -two kinds of options: -* special options to be resolved inside the script and that may do some -advanced checks; this should later turn into a set of specific `cmake` -variable declarations +| Option Name | Since | Type | Default | Short Description | +| :----------------------------------------------------------- | :---: | :-------: | :--------: | ----------------- | +| [`CMAKE_INSTALL_PREFIX`](#cmake_install_prefix) | 1.3.0 | `STRING` | OFF | Standard CMake variable that establishes the root directory for installation, inside of which a GNU/POSIX compatible directory layout will be used. | +| [`CYGWIN_USE_POSIX`](#cygwin_use_posix) | 1.2.0 | `BOOL` | OFF | Determines when to compile on Cygwin using POSIX API. | +| [`ENABLE_APPS`](#enable_apps) | 1.3.3 | `BOOL` | ON | Enables compiling sample applications (srt-live-trasnmit, etc.). | +| [`ENABLE_CXX_DEPS`](#enable_cxx_deps) | 1.3.2 | `BOOL` | OFF | The `pkg-confg` file (`srt.pc`) will be generated with the `libstdc++` library as a dependency. | +| [`ENABLE_CXX11`](#enable_cxx11) | 1.2.0 | `BOOL` | ON | Enable compiling in C++11 mode for those parts that may require it. Default: ON except for GCC<4.7 | +| [`ENABLE_CODE_COVERAGE`](#enable_code_coverage) | 1.4.0 | `BOOL` | OFF | Enables instrumentation for code coverage. | +| [`ENABLE_DEBUG`](#enable_debug>) | 1.2.0 | `INT` | ON | Allows release/debug control through the `CMAKE_BUILD_TYPE` variable. | +| [`ENABLE_ENCRYPTION`](#enable_encryption) | 1.3.3 | `BOOL` | ON | Enables encryption feature enabled, with dependency on an external encryption library. | +| [`ENABLE_GETNAMEINFO`](#enable_getnameinfo) | 1.3.0 | `BOOL` | OFF | Enables the use of `getnameinfo` to allow using reverse DNS to resolve an internal IP address into a readable internet domain name. | +| [`ENABLE_HAICRYPT_LOGGING`](#enable_haicrypt_logging) | 1.3.1 | `BOOL` | OFF | Enables logging in the *haicrypt* module, which serves as a connector to an encryption library. | +| [`ENABLE_HEAVY_LOGGING`](#enable_heavy_logging) | 1.3.0 | `BOOL` | OFF | Enables heavy logging instructions in the code that occur often and cover many detailed aspects of library behavior. Default: OFF in release mode. | +| [`ENABLE_INET_PTON`](#enable_inet_pton) | 1.3.2 | `BOOL` | ON | Enables usage of the `inet_pton` function used to resolve the network endpoint name into an IP address. | +| [`ENABLE_LOGGING`](#enable_logging) | 1.2.0 | `BOOL` | ON | Enables normal logging, including errors. | +| [`ENABLE_MONOTONIC_CLOCK`](#enable_monotonic_clock) | 1.4.0 | `BOOL` | ON* | Enforces the use of `clock_gettime` with a monotonic clock that is independent of the currently set time in the system. | +| [`ENABLE_NEW_RCVBUFFER`](#enable_new_rcvbuffer) | 1.4.5 | `BOOL` | ON | Enables the new implementation of the receiver buffer with behavior and code improvements (in dev build 1.4.5 only). | +| [`ENABLE_PROFILE`](#enable_profile) | 1.2.0 | `BOOL` | OFF | Enables code instrumentation for profiling (only for GNU-compatible compilers). | +| [`ENABLE_RELATIVE_LIBPATH`](#enable_relative_libpath) | 1.3.2 | `BOOL` | OFF | Enables adding a relative path to a library for linking against a shared SRT library by reaching out to a sibling directory. | +| [`ENABLE_SHARED`](#enable_shared--enable_static) | 1.2.0 | `BOOL` | ON | Enables building SRT as a shared library | +| [`ENABLE_SHOW_PROJECT_CONFIG`](#enable_show_project_config) | 1.4.5 | `BOOL` | OFF | When ON, the project configuration is displayed at the end of the CMake Configuration Step (in dev build 1.4.5 only). | +| [`ENABLE_STATIC`](#enable_shared--enable_static) | 1.3.0 | `BOOL` | ON | Enables building SRT as a tatic library | +| [`ENABLE_STDCXX_SYNC`](#enable_stdcxx_sync) | 1.4.2 | `BOOL` | ON* | Enables the standard C++11 `thread` and `chrono` libraries to be used by SRT instead of the `pthreads`. | +| [`ENABLE_TESTING`](#enable_testing) | 1.3.0 | `BOOL` | OFF | Enables compiling of developer testing applications (srt-test-live, etc.). | +| [`ENABLE_THREAD_CHECK`](#enable_thread_check) | 1.3.0 | `BOOL` | OFF | Enables `#include `, which implements `THREAD_*` macros" to support better thread debugging. | +| [`ENABLE_UNITTESTS`](#enable_unittests) | 1.3.2 | `BOOL` | OFF | Enables building unit tests. | +| [`OPENSSL_CRYPTO_LIBRARY`](#openssl_crypto_library) | 1.3.0 | `STRING` | OFF | Configures the path to an OpenSSL Crypto library. | +| [`OPENSSL_INCLUDE_DIR`](#openssl_include_dir) | 1.3.0 | `STRING` | OFF | Configures the path to include files for an OpenSSL library. | +| [`OPENSSL_SSL_LIBRARY`](#openssl_ssl_library) | 1.3.0 | `STRING` | OFF | Configures the path to an OpenSSL SSL library. | +| [`PKG_CONFIG_EXECUTABLE`](#pkg_config_executable) | 1.3.0 | `BOOL` | OFF | Configures the path to the `pkg-config` tool. | +| [`PTHREAD_INCLUDE_DIR`](#pthread_include_dir) | 1.3.0 | `STRING` | OFF | Configures the path to include files for a pthread library. | +| [`PTHREAD_LIBRARY`](#pthread_library) | 1.3.0 | `STRING` | OFF | Configures the path to a pthread library. | +| [`USE_BUSY_WAITING`](#use_busy_waiting) | 1.3.3 | `BOOL` | OFF | Enables more accurate sending times at the cost of potentially higher CPU load. | +| [`USE_CXX_STD`](#use_cxx_std) | 1.4.2 | `STRING` | OFF | Enforces using a particular C++ standard (11, 14, 17, etc.) when compiling. | +| [`USE_ENCLIB`](#use_enclib) | 1.3.3 | `STRING` | openssl | Encryption library to be used (`openssl`, `gnutls`, `mbedtls`). | +| [`USE_GNUSTL`](#use_gnustl) | 1.3.4 | `BOOL` | OFF | Use `pkg-config` with the `gnustl` package name to extract the header and library path for the C++ standard library. | +| [`USE_OPENSSL_PC`](#use_openssl_pc) | 1.3.0 | `BOOL` | ON | Use `pkg-config` to find OpenSSL libraries. | +| [`USE_STATIC_LIBSTDCXX`](#use_static_libstdcxx) | 1.2.0 | `BOOL` | OFF | Enforces linking the SRT library against the static libstdc++ library. | +| [`WITH_COMPILER_PREFIX`](#with_compiler_prefix) | 1.3.0 | `STRING` | OFF | Sets C/C++ toolchains as `` and ``, overriding the default compiler. | +| [`WITH_COMPILER_TYPE`](#with_compiler_type) | 1.3.0 | `STRING` | OFF | Sets the compiler type to be used (values: gcc, cc, clang, etc.). | +| [`WITH_EXTRALIBS`](#with_extralibs) | 1.3.0 | `STRING` | OFF | Option required for unusual situations when a platform-specific workaround is needed and some extra libraries must be passed explicitly for linkage. | +| [`WITH_SRT_NAME`](#with_srt_name) | 1.3.0 | `STRING` | OFF | Configure the SRT library name adding a custom `` | +| | | | | | + + +\* See the option description for more details. + +## Using CMake + +If you choose to use CMake directly for the build configuration stage, you must +specify option values in the CMake format: + +`-D