From 20043be2f712564130ae24135d8cf659c5c881e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Nov 2020 14:07:06 +0100 Subject: [PATCH 001/174] [core] Fixed a bug that responded a repeated conclusion HS with rejection --- srtcore/core.cpp | 274 +++++++++++++++++++++++++++++------------------ srtcore/core.h | 1 + 2 files changed, 170 insertions(+), 105 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 098198f46..e37cd1e77 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1587,7 +1587,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) { @@ -1598,24 +1598,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. @@ -1635,50 +1635,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 @@ -1697,18 +1697,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 @@ -1722,8 +1722,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); @@ -1736,14 +1736,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 @@ -1753,7 +1753,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)) @@ -1767,7 +1767,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."); } } @@ -1779,9 +1779,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; } @@ -2207,7 +2207,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, @@ -4319,6 +4319,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 << "processRendezvous: 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; + + 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 + { + 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 << "processRendezvous: 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 << "processRendezvous: 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, bool synchro, EReadStatus rst, CPacket& w_reqpkt) @@ -4415,73 +4494,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 @@ -8538,7 +8555,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 @@ -8573,7 +8590,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; @@ -8611,6 +8628,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; @@ -8619,7 +8637,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); @@ -10564,9 +10581,54 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // reused for the connection rejection response (see URQ_ERROR_REJECT set // as m_iReqType). + if (result == 0) + { + // 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)); + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + EConnectStatus conn = CONN_ACCEPT; + + if (hs.m_iVersion >= HS_VERSION_SRT1) + { + conn = craftKmResponse((kmdata), (kmdatasize)); + } + else + { + kmdatasize = 0; + } + + if (conn != CONN_ACCEPT) + return conn; + + packet.setLength(m_iMaxSRTPayloadSize); + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, + kmdata, kmdatasize, + (packet), (m_ConnReq))) + { + 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()); + m_pSndQueue->sendto(addr, 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=" @@ -10579,6 +10641,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 962462100..d33026b64 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -551,6 +551,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); From 71268420be6032eb2478b11a0a43338d5357c005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 24 Nov 2020 12:01:55 +0100 Subject: [PATCH 002/174] Minor comment update --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 761bfdc33..5a8c193b1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4420,11 +4420,11 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) memcpy((aw_kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); w_kmdatasize = 1; } - break; + break; // Treat as ACCEPT in general; might change to REJECT on enforced-encryption default: // Remaining values: - // UNSECURED: should not fall here at alll + // 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) { From 6226f3556319fc05b18664cefda5fde2a8c73f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 24 Nov 2020 17:42:23 +0100 Subject: [PATCH 003/174] Fixed proper crafting of the repeated handshake response --- srtcore/api.cpp | 7 ++++++- srtcore/api.h | 2 +- srtcore/core.cpp | 31 ++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 2715ae7b6..66b09b2f9 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..1a4927c5c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -232,7 +232,7 @@ friend class CRendezvousQueue; /// @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 5a8c193b1..99f6832d0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4412,7 +4412,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) case SRT_KM_S_BADSECRET: { HLOGC(cnlog.Debug, - log << "processRendezvous: No KMX recorded, status = " + 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, @@ -4432,7 +4432,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) // 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=" + log << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" << KmStateStr(m_pCryptoControl->m_RcvKmState) << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); return CONN_REJECT; @@ -4452,14 +4452,14 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) } HLOGC(cnlog.Debug, - log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + 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 << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); + HLOGC(cnlog.Debug, log << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP"); w_kmdatasize = 0; } @@ -10813,7 +10813,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,7 +10854,17 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // reused for the connection rejection response (see URQ_ERROR_REJECT set // as m_iReqType). - if (result == 0) + // 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 @@ -10870,7 +10881,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iVersion >= HS_VERSION_SRT1) { - conn = craftKmResponse((kmdata), (kmdatasize)); + // Always attach extension. + hs.m_extension = true; + conn = acpu->craftKmResponse((kmdata), (kmdatasize)); } else { @@ -10881,9 +10894,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) return conn; packet.setLength(m_iMaxSRTPayloadSize); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, + if (!acpu->createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (packet), (m_ConnReq))) + (packet), (hs))) { HLOGC(cnlog.Debug, log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); From f68578d901a47e04d79e76c25c0ff5b179ca705d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 27 Nov 2020 11:17:28 +0100 Subject: [PATCH 004/174] Completed doxygen changes --- srtcore/api.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 1a4927c5c..8ad6bb3fd 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -229,6 +229,8 @@ 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, From 8e8cdac4ac73d5f066405b052bcf6f56471e7160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Nov 2020 09:32:17 +0100 Subject: [PATCH 005/174] Prepared changes for dispatch-to-acceptor (no build) --- srtcore/core.cpp | 17 +++++++++++++---- srtcore/core.h | 2 ++ srtcore/queue.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/queue.h | 10 ++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 414b51765..ce0d75c51 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5997,6 +5997,14 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); + if (!createSendHSResponse(kmdata, kmdatasize, peer, (w_hs))) + { + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } +} + +bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& peer, CHandShake& w_hs) ATR_NOTHROW +{ // 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, @@ -6010,11 +6018,11 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, - log << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + log << "createSendHSResponse: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) { - LOGC(cnlog.Error, log << "acceptAndRespond: error creating handshake response"); - throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + LOGC(cnlog.Error, log << "createSendHSResponse: error creating handshake response"); + return false; } // Set target socket ID to the value from received handshake's source ID. @@ -6027,7 +6035,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, CHandShake debughs; debughs.load_from(response.m_pcData, response.getLength()); HLOGC(cnlog.Debug, - log << CONID() << "acceptAndRespond: sending HS from agent @" + log << CONID() << "createSendHSResponse: sending HS from agent @" << debughs.m_iID << " to peer @" << response.m_iID << "HS:" << debughs.show()); } @@ -6039,6 +6047,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // coming as connected, but continue repeated handshake until finally // received the listener's handshake. m_pSndQueue->sendto(peer, response); + return true; } // This function is required to be called when a caller receives an INDUCTION diff --git a/srtcore/core.h b/srtcore/core.h index cc4d8ccf2..911619146 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -365,6 +365,7 @@ class CUDT } SRTSOCKET socketID() const { return m_SocketID; } + SRTSOCKET peerID() const { return m_PeerID; } static CUDT* getUDTHandle(SRTSOCKET u); static std::vector existingSockets(); @@ -599,6 +600,7 @@ class CUDT /// @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); + bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& peer, CHandShake& w_hs) ATR_NOTHROW; bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt); /// Close the opened UDT entity. diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 3f7f18e13..8d34cddb7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -773,16 +773,27 @@ CUDT *CHash::lookup(int32_t id) return NULL; } +CUDT* CHash::lookupPeer(int32_t peerid) +{ + // Decode back the socket ID if it has that peer + int32_t id = map_get(m_RevPeerMap, peerid, -1); + if (id == -1) + return NULL; // no such peer id + return lookup(id); +} + void CHash::insert(int32_t id, CUDT *u) { CBucket *b = m_pBucket[id % m_iHashSize]; CBucket *n = new CBucket; n->m_iID = id; + n->m_iPeerID = u->peerID(); n->m_pUDT = u; n->m_pNext = b; m_pBucket[id % m_iHashSize] = n; + m_RevPeerMap[u->peerID()] = id; } void CHash::remove(int32_t id) @@ -799,6 +810,7 @@ void CHash::remove(int32_t id) else p->m_pNext = b->m_pNext; + m_RevPeerMap.erase(b->m_iPeerID); delete b; return; @@ -1402,11 +1414,44 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } + else + { + if (worker_TryAcceptedSocket(unit, addr)) + return CONN_CONTINUE; + } // If there's no listener waiting for the packet, just store it into the queue. return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. } +bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) +{ + // We are working with a possibly HS packet... check that. + CPacket& pkt = unit->m_Packet; + + if (pkt.getLength() < CHandShake::m_iContentSize || !pkt.isControl(UMSG_HANDSHAKE)) + return false; + + CHandShake hs; + if (0 != hs.load_from(pkt.data(), pkt.size())) + return false; + + if (hs.m_iReqType != URQ_CONCLUSION) + return false; + + // Ok, at last we have a peer ID info + int32_t peerid = hs.m_iID; + + // Now search for a socket that has this peer ID + CUDT* u = m_pHash->lookupPeer(peerid); + if (!u) + return false; // no socket has that peer in this multiplexer + + // CRAFT KMX DATA FOR RESPONSE + + return u->createSendHSResponse(kmdata, kmdatasize, (hs)); +} + EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { CUDT *u = m_pHash->lookup(id); diff --git a/srtcore/queue.h b/srtcore/queue.h index d14b8728a..d2dfb9cdf 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -292,6 +292,12 @@ class CHash CUDT* lookup(int32_t id); + /// Look for a UDT instance from the hash table by source ID + /// @param [in] peerid socket ID of the peer reported as source ID + /// @return Pointer to a UDT instance where m_PeerID == peerid, or NULL if not found + + CUDT* lookupPeer(int32_t peerid); + /// Insert an entry to the hash table. /// @param [in] id socket ID /// @param [in] u pointer to the UDT instance @@ -307,6 +313,7 @@ class CHash struct CBucket { int32_t m_iID; // Socket ID + int32_t m_iPeerID; // Peer ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -314,6 +321,8 @@ class CHash int m_iHashSize; // size of hash table + std::map m_RevPeerMap; + private: CHash(const CHash&); CHash& operator=(const CHash&); @@ -489,6 +498,7 @@ friend class CUDTUnited; 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); + bool worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr); private: CUnitQueue m_UnitQueue; // The received packet queue From 84e37b6877af5a1c3f50121b85aa222662a910e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Nov 2020 16:37:01 +0100 Subject: [PATCH 006/174] Added crafting KMX response to call handshake --- srtcore/queue.cpp | 7 +++++-- testing/testmedia.cpp | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8d34cddb7..487502b1e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1447,9 +1447,12 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (!u) return false; // no socket has that peer in this multiplexer - // CRAFT KMX DATA FOR RESPONSE + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + if (u->craftKmResponse((kmdata), (kmdatasize)) == CONN_ACCEPT) + return false; - return u->createSendHSResponse(kmdata, kmdatasize, (hs)); + return u->createSendHSResponse(kmdata, kmdatasize, addr, (hs)); } EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 4c5a92462..d9861a96c 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -545,6 +545,9 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + /// TESTING + //srt_close(m_bindsock); + #if ENABLE_EXPERIMENTAL_BONDING if (m_sock & SRTGROUP_MASK) { From 80a4cbf4e5fa8727cb9cfe70e35797b256315503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Dec 2020 11:03:46 +0100 Subject: [PATCH 007/174] Fixed HS ext for HSv5. Blocked testing stuff --- srtcore/core.cpp | 10 ++++++++++ srtcore/queue.cpp | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6fa9682f7..7de2f0ce7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4992,6 +4992,16 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. + /// TESTING - unblock if necessary. + /// This part fictionally "loses" incoming conclusion HS 5 times. + /* + static int fail_count = 5; + if (--fail_count) + { + LOGC(cnlog.Note, log << "postConnect: FAKE LOSS HS conclusion message"); + return CONN_CONTINUE; + } // */ + // This procedure isn't being executed in rendezvous because // in rendezvous it's completed before calling this function. if (!rendezvous) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 4064b06a0..77e1ddaa7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1448,7 +1448,14 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc else { if (worker_TryAcceptedSocket(unit, addr)) + { + HLOGC(cnlog.Debug, log << "connection request to existing peer succeeded"); return CONN_CONTINUE; + } + else + { + HLOGC(cnlog.Debug, log << "connection request to an accepted socket failed. Will retry RDV or store"); + } } // If there's no listener waiting for the packet, just store it into the queue. @@ -1470,6 +1477,9 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (hs.m_iReqType != URQ_CONCLUSION) return false; + if (hs.m_iVersion >= CUDT::HS_VERSION_SRT1) + hs.m_extension = true; + // Ok, at last we have a peer ID info int32_t peerid = hs.m_iID; @@ -1478,10 +1488,16 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (!u) return false; // no socket has that peer in this multiplexer + HLOGC(cnlog.Debug, log << "FOUND accepted socket @" << u->m_SocketID << " that is a peer for -@" + << peerid << " - DISPATCHING to it to resend HS response"); + uint32_t kmdata[SRTDATA_MAXSIZE]; size_t kmdatasize = SRTDATA_MAXSIZE; - if (u->craftKmResponse((kmdata), (kmdatasize)) == CONN_ACCEPT) + if (u->craftKmResponse((kmdata), (kmdatasize)) != CONN_ACCEPT) + { + HLOGC(cnlog.Debug, log << "craftKmResponse: failed"); return false; + } return u->createSendHSResponse(kmdata, kmdatasize, addr, (hs)); } From 12b745556b733267c8bc487afe9ebd09acb2db78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 12:04:17 +0100 Subject: [PATCH 008/174] Fixed: rewritten negotiated values into the handshake and properly sent --- srtcore/core.cpp | 60 +++++++++++++++++++++++++----------------------- srtcore/core.h | 11 +++++++-- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 99f6832d0..68dc1119c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5874,6 +5874,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"); @@ -5883,45 +5902,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, agent.family(), 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; @@ -6034,9 +6036,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 @@ -6055,7 +6054,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 @@ -10875,6 +10874,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) 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; @@ -10907,7 +10909,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // Send the crafted handshake HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + acpu->addressAndSend((packet)); } } diff --git a/srtcore/core.h b/srtcore/core.h index 005a32d08..0bb7d59d1 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -595,11 +595,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 bb7b41c9923ab86cfdf8ba46f84eff2ca8693851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 14:17:46 +0100 Subject: [PATCH 009/174] Removed testing entry --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 53c2cbf24..dd5cb77a3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6040,7 +6040,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. - return; + //return; if (!createSendHSResponse(kmdata, kmdatasize, (w_hs))) { From abfbf441475a4fb6e0b0289b6f462573fa035861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 14:26:56 +0100 Subject: [PATCH 010/174] Fixed: do not try accepted socket if wrong address --- srtcore/queue.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index ef4b61d47..46715a539 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1449,7 +1449,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc { if (worker_TryAcceptedSocket(unit, addr)) { - HLOGC(cnlog.Debug, log << "connection request to existing peer succeeded"); + HLOGC(cnlog.Debug, log << "connection request to an accepted socket succeeded"); return CONN_CONTINUE; } else @@ -1505,7 +1505,8 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (addr != u->m_PeerAddr) { HLOGC(cnlog.Debug, log << "worker_TryAcceptedSocket: accepted socket has a different address: " - << u->m_PeerAddr.str() << " than the incoming HS request: " << addr.str()); + << u->m_PeerAddr.str() << " than the incoming HS request: " << addr.str() << " - POSSIBLE ATTACK"); + return false; } return u->createSendHSResponse(kmdata, kmdatasize, (hs)); From 3f6e6acb11d9ace119cdbbd45e396390638c2a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 1 Jun 2021 17:32:37 +0200 Subject: [PATCH 011/174] [core] Fixed: closing socket should mark and signal so that srt_connect call can exit immediately --- srtcore/api.cpp | 8 ++++++++ srtcore/core.cpp | 6 ++++++ srtcore/queue.cpp | 5 +++++ srtcore/queue.h | 2 ++ 4 files changed, 21 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index aee13f389..ab57fa42b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1964,6 +1964,14 @@ int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); + // This socket might be currently during reading from + // the receiver queue as called from `srt_connect` API. + // Until this procedure breaks, locking s->m_ControlLock + // would have to wait. Mark it closing right now and force + // the receiver queue to stop waiting immediately. + s->m_pUDT->m_bClosing = true; + s->m_pUDT->m_pRcvQueue->kick(); + ScopedLock socket_cg(s->m_ControlLock); HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7a337cf41..e42a276f3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3659,6 +3659,12 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable. } + // The queue could have been kicked by the close() API call, + // if so, interrupt immediately. + if (m_bClosing || m_bBroken) + break; + + HLOGC(cnlog.Debug, log << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index b9d7ed02e..fa135619e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1758,6 +1758,11 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } +void srt::CRcvQueue::kick() +{ + CSync::lock_broadcast(m_BufferCond, m_BufferLock); +} + void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) { UniqueLock bufferlock(m_BufferLock); diff --git a/srtcore/queue.h b/srtcore/queue.h index ee05440c8..a4460ba5d 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -566,6 +566,8 @@ class CRcvQueue void storePkt(int32_t id, CPacket* pkt); + void kick(); + private: sync::Mutex m_LSLock; CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity From 7164030f4f2f4ff4e5a9adf5f5ac5853a8b13b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Jun 2021 11:14:28 +0200 Subject: [PATCH 012/174] Fixed problem: test if socket is in this blocking-connecting state before changing the flag. Otherwise it would confuse the closing function when used on a connected socket --- srtcore/api.cpp | 42 +++++++++++++++++++++++++++------ test/test_file_transmission.cpp | 9 ++++++- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ab57fa42b..2b77256b5 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1964,13 +1964,41 @@ int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); - // This socket might be currently during reading from - // the receiver queue as called from `srt_connect` API. - // Until this procedure breaks, locking s->m_ControlLock - // would have to wait. Mark it closing right now and force - // the receiver queue to stop waiting immediately. - s->m_pUDT->m_bClosing = true; - s->m_pUDT->m_pRcvQueue->kick(); + // The check for whether m_pRcvQueue isn't NULL is safe enough; + // it can either be NULL after socket creation and without binding + // and then once it's assigned, it's never reset to NULL even when + // destroying the socket. + CUDT* e = s->m_pUDT; + if (e->m_pRcvQueue && e->m_bConnecting && !e->m_bConnected) + { + // Workaround for a design flaw. + // It's to work around the case when the socket is being + // closed in another thread while it's in the process of + // connecting in the blocking mode, that is, it runs the + // loop in `CUDT::startConnect` whole time under the lock + // of CUDT::m_ConnectionLock and CUDTSocket::m_ControlLock + // this way blocking the `srt_close` API call from continuing. + // We are setting here the m_bClosing flag prematurely so + // that the loop may check this flag periodically and exit + // immediately if it's set. + // + // The problem is that this flag shall NOT be set in case + // when you have a CONNECTED socket because not only isn't it + // not a problem in this case, but also it additionally + // turns the socket in a "confused" state in which it skips + // vital part of closing itself and therefore runs an infinite + // loop when trying to purge the sender buffer of the closing + // socket. + // + // XXX Consider refax on CUDT::startConnect and removing the + // connecting loop there and replace the "blocking mode specific" + // connecting procedure with delegation to the receiver queue, + // which will be then common with non-blocking mode, and synchronize + // the blocking through a CV. + + e->m_bClosing = true; + e->m_pRcvQueue->kick(); + } ScopedLock socket_cg(s->m_ControlLock); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 790555eb7..21a920741 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -17,6 +17,7 @@ #endif #include "srt.h" +#include "threadname.h" #include #include @@ -74,6 +75,7 @@ TEST(Transmission, FileUpload) auto client = std::thread([&] { + ThreadName::set("TEST-in"); sockaddr_in remote; int len = sizeof remote; const SRTSOCKET accepted_sock = srt_accept(sock_lsn, (sockaddr*)&remote, &len); @@ -93,7 +95,12 @@ TEST(Transmission, FileUpload) for (;;) { int n = srt_recv(accepted_sock, buf.data(), 1456); - ASSERT_NE(n, SRT_ERROR); + EXPECT_NE(n, SRT_ERROR); + if (n == -1) + { + std::cerr << "UNEXPECTED ERROR: " << srt_getlasterror_str() << std::endl; + break; + } if (n == 0) { break; From cdca85d892a34776e2227d8c3338d780541b631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Jun 2021 11:25:44 +0200 Subject: [PATCH 013/174] Added UT for the closing case --- test/test_common.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/test_common.cpp b/test/test_common.cpp index a4b8c033a..992b93893 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -1,6 +1,11 @@ #include #include +#include + +#include +#include + #include "gtest/gtest.h" #include "utilities.h" #include "common.h" @@ -65,3 +70,49 @@ TEST(CIPAddress, IPv4_in_IPv6_pton) test_cipaddress_pton(peer_ip, AF_INET6, ip); } + + +TEST(SRTAPI, SyncRendezvousHangs) { + ASSERT_EQ(srt_startup(), 0); + + int yes = 1; + + SRTSOCKET m_bindsock = srt_create_socket(); + ASSERT_NE(m_bindsock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_bindsock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes), 0); + + const int connection_timeout_ms = 1000; // rendezvous timeout is x10 hence 10seconds + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), 0); + + sockaddr_in local_sa={}; + local_sa.sin_family = AF_INET; + local_sa.sin_port = htons(9999); + local_sa.sin_addr.s_addr = INADDR_ANY; + + sockaddr_in peer_sa= {}; + peer_sa.sin_family = AF_INET; + peer_sa.sin_port = htons(9998); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &peer_sa.sin_addr), 1); + + uint64_t duration = 0; + + std::thread close_thread([&m_bindsock, &duration] { + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait till srt_rendezvous is called + auto start = std::chrono::steady_clock::now(); + srt_close(m_bindsock); + auto end = std::chrono::steady_clock::now(); + + duration = std::chrono::duration_cast(end - start).count(); + }); + + ASSERT_EQ(srt_rendezvous(m_bindsock, (sockaddr*)&local_sa, sizeof local_sa, + (sockaddr*)&peer_sa, sizeof peer_sa), SRT_ERROR); + + close_thread.join(); + ASSERT_LE(duration, 1); + srt_close(m_bindsock); + srt_cleanup(); +} From 7923aae10e274d1c64258b0f71e9e951b11aca7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 25 Aug 2022 10:45:13 +0200 Subject: [PATCH 014/174] Added a UT that confirms the bug. Added an option setter utility. --- apps/apputil.hpp | 37 ++++++++++++++++++++++- test/test_epoll.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index acb28d076..c6e013f02 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -20,6 +20,7 @@ #include "netinet_any.h" #include "utilities.h" +#include "srt.h" #if _WIN32 @@ -332,8 +333,42 @@ 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); - const char* SRTClockTypeStr(); void PrintLibVersion(); + +namespace srt +{ + +struct OptionSetterProxy +{ + SRTSOCKET s = -1; + int result = 0; + + struct OptionProxy + { + OptionSetterProxy& parent; + SRT_SOCKOPT opt; + + template + OptionProxy& operator=(Type&& val) + { + Type vc(val); + srt_setsockflag(parent.s, opt, &vc, sizeof vc); + return *this; + } + }; + + OptionProxy operator[](SRT_SOCKOPT opt) + { + return OptionProxy {*this, opt}; + } +}; + +inline OptionSetterProxy setopt(SRTSOCKET socket) +{ + return OptionSetterProxy {socket}; +} + +} #endif // INC_SRT_APPCOMMON_H diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index d5e044d7e..497bce692 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -6,7 +6,7 @@ #include "gtest/gtest.h" #include "api.h" #include "epoll.h" - +#include "apputil.hpp" using namespace std; using namespace srt; @@ -561,6 +561,76 @@ TEST(CEPoll, ThreadedUpdate) EXPECT_EQ(srt_cleanup(), 0); } +TEST(CEPoll, LateListenerReady) +{ + int server_sock = srt_create_socket(), caller_sock = 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(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, 1); + + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + +// This macro is to manipulate with the moment when the call is made +// towards the eid subscription. If 1, then the call is made first, +// and then subsciption after a 1s time. Set it to 0 to see how it +// works when the subscription is made first, so the readiness is from +// the listener changing the state. +#define LATE_CALL 1 + +#if LATE_CALL + + // We don't need the caller to be async, it can hang up here. + auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + +#endif + this_thread::sleep_for(chrono::milliseconds(1000)); + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + +#if !LATE_CALL + + // We don't need the caller to be async, it can hang up here. + auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + +#endif + + // And see now if the waiting accepted socket reports it. + SRT_EPOLL_EVENT fdset[1]; + EXPECT_EQ(srt_epoll_uwait(eid, fdset, 1, 5000), 1); + + sockaddr_in scl; + int sclen = sizeof scl; + SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + srt_epoll_release(eid); + srt_close(sock); + srt_close(server_sock); + srt_close(caller_sock); +} + class TestEPoll: public testing::Test { From 6cf64f5944408311fd0930f14dff6dbd87995cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 25 Aug 2022 14:55:51 +0200 Subject: [PATCH 015/174] Fixed clang errors. Added compiler flags to configure to prevent changes by google test --- apps/apputil.hpp | 13 +++++++----- configure-data.tcl | 53 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index c6e013f02..c79cf984e 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -333,6 +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); + const char* SRTClockTypeStr(); void PrintLibVersion(); @@ -342,12 +343,14 @@ namespace srt struct OptionSetterProxy { - SRTSOCKET s = -1; - int result = 0; + SRTSOCKET s; + int result = -1; + + OptionSetterProxy(SRTSOCKET ss): s(ss) {} struct OptionProxy { - OptionSetterProxy& parent; + const OptionSetterProxy& parent; SRT_SOCKOPT opt; template @@ -359,7 +362,7 @@ struct OptionSetterProxy } }; - OptionProxy operator[](SRT_SOCKOPT opt) + OptionProxy operator[](SRT_SOCKOPT opt) const { return OptionProxy {*this, opt}; } @@ -367,7 +370,7 @@ struct OptionSetterProxy inline OptionSetterProxy setopt(SRTSOCKET socket) { - return OptionSetterProxy {socket}; + return OptionSetterProxy(socket); } } diff --git a/configure-data.tcl b/configure-data.tcl index eace99da1..40500a721 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -190,7 +190,24 @@ proc preprocess {} { } } -proc GetCompilerCommand {} { +set compiler_map { + cc c++ + gcc g++ +} + +proc GetCompilerCmdName {compiler lang} { + if {$lang == "c++"} { + if { [dict exists $::compiler_map $compiler] } { + return [dict get $::compiler_map $compiler] + } + + return ${compiler}++ + } + + return $compiler +} + +proc GetCompilerCommand { {lang {}} } { # Expect that the compiler was set through: # --with-compiler-prefix # --cmake-c[++]-compiler @@ -203,21 +220,25 @@ proc GetCompilerCommand {} { if { [info exists ::optval(--with-compiler-prefix)] } { set prefix $::optval(--with-compiler-prefix) - return ${prefix}$compiler + return ${prefix}[GetCompilerCmdName $compiler $lang] } else { - return $compiler + return [GetCompilerCmdName $compiler $lang] } - if { [info exists ::optval(--cmake-c-compiler)] } { - return $::optval(--cmake-c-compiler) + if { $lang != "c++" } { + if { [info exists ::optval(--cmake-c-compiler)] } { + return $::optval(--cmake-c-compiler) + } } - if { [info exists ::optval(--cmake-c++-compiler)] } { - return $::optval(--cmake-c++-compiler) - } + if { $lang != "c" } { + if { [info exists ::optval(--cmake-c++-compiler)] } { + return $::optval(--cmake-c++-compiler) + } - if { [info exists ::optval(--cmake-cxx-compiler)] } { - return $::optval(--cmake-cxx-compiler) + if { [info exists ::optval(--cmake-cxx-compiler)] } { + return $::optval(--cmake-cxx-compiler) + } } puts "NOTE: Cannot obtain compiler, assuming toolchain file will do what's necessary" @@ -283,6 +304,18 @@ proc postprocess {} { } else { puts "CONFIGURE: default compiler used" } + + # Complete the variables before calling cmake, otherwise it might not work + + if { [info exists ::optval(--with-compiler-type)] } { + if { ![info exists ::optval(--cmake-c-compiler)] } { + lappend ::cmakeopt "-DCMAKE_C_COMPILER=[GetCompilerCommand c]" + } + + if { ![info exists ::optval(--cmake-c++-compiler)] } { + lappend ::cmakeopt "-DCMAKE_CXX_COMPILER=[GetCompilerCommand c++]" + } + } } if { $::srt_name != "" } { From 068e8a9b3de03296be6222b8788dd53ed8f62542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 26 Aug 2022 13:27:19 +0200 Subject: [PATCH 016/174] Added checking of accept candidates. In-group check disabled and left dead code (this solution deadlocks) --- srtcore/api.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/api.h | 4 ++++ srtcore/core.cpp | 20 ++++++++++++++++ srtcore/group.h | 5 ++++ test/test_epoll.cpp | 4 ++++ 5 files changed, 91 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d575cd968..642067a3f 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -835,6 +835,64 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return 1; } +SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() +{ + // You need to check EVERY socket that has been queued + // and verify its internals. With independent socket the + // matter is simple - if it's present, you light up the + // SRT_EPOLL_ACCEPT flag. + +//#if !ENABLE_BONDING +#if 1 + ScopedLock accept_lock (m_AcceptLock); + + // Make it simplified here - nonempty container = have acceptable sockets. + // Might make sometimes spurious acceptance, but this can also happen when + // the incoming accepted socket was suddenly broken. + return m_QueuedSockets.empty() ? 0 : int(SRT_EPOLL_ACCEPT); + +#else // Could do #endif here, but the compiler would complain about unreachable code. + + set sockets_copy; + { + ScopedLock accept_lock (m_AcceptLock); + sockets_copy = m_QueuedSockets; + } + return CUDT::uglobal().checkQueuedSocketsEvents(sockets_copy); + +#endif +} + +#if ENABLE_BONDING +int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) +{ + SRT_EPOLL_T flags = 0; + + // But with the member sockets an appropriate check must be + // done first: if this socket belongs to a group that is + // already in the connected state, you should light up the + // SRT_EPOLL_UPDATE flag instead. This flag is only for + // internal informing the waiters on the listening sockets + // that they should re-read the group list and re-check readiness. + + // Now we can do lock once and for all + //ScopedLock glk (m_GlobControlLock); // XXX DEADLOCKS + + for (set::iterator i = sockets.begin(); i != sockets.end(); ++i) + { + CUDTSocket* s = locateSocket_LOCKED(*i); + if (!s) + continue; // wiped in the meantime - ignore + if (s->m_GroupOf && s->m_GroupOf->groupConnected()) + flags |= SRT_EPOLL_UPDATE; + else + flags |= SRT_EPOLL_ACCEPT; + } + + return flags; +} +#endif + // static forwarder int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { diff --git a/srtcore/api.h b/srtcore/api.h index ca812bf9e..6254812a4 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -158,6 +158,8 @@ class CUDTSocket unsigned int m_uiBackLog; //< maximum number of connections in queue + SRT_EPOLL_T getListenerEvents(); + // XXX A refactoring might be needed here. // There are no reasons found why the socket can't contain a list iterator to a @@ -264,6 +266,8 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); + int checkQueuedSocketsEvents(const std::set& sockets); + 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 93224f418..18b9c013c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11515,6 +11515,26 @@ void srt::CUDT::addEPoll(const int eid) m_sPollID.insert(eid); leaveCS(uglobal().m_EPoll.m_EPollLock); + //* + if (m_bListening) + { + // A listener socket can only get readiness on SRT_EPOLL_ACCEPT + // (which has the same value as SRT_EPOLL_IN), or sometimes + // also SRT_EPOLL_UPDATE. All interesting fields for that purpose + // are contained in the CUDTSocket class, so redirect there. + SRT_EPOLL_T events = m_parent->getListenerEvents(); + + // Only light up the events that were returned, do nothing if none is ready, + // the "no event" state is the default. + if (events) + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, events, true); + + // You don't check anything else here - a listener socket can be only + // used for listening and nothing else. + return; + } + // */ + if (!stillConnected()) return; diff --git a/srtcore/group.h b/srtcore/group.h index 1bd84aeda..c5f147f23 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -206,6 +206,11 @@ class CUDTGroup return m_Group.empty(); } + bool groupConnected() + { + return m_bConnected; + } + void setGroupConnected(); int send(const char* buf, int len, SRT_MSGCTRL& w_mc); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 497bce692..47b488b88 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -563,6 +563,8 @@ TEST(CEPoll, ThreadedUpdate) TEST(CEPoll, LateListenerReady) { + ASSERT_EQ(srt_startup(), 0); + int server_sock = srt_create_socket(), caller_sock = srt_create_socket(); sockaddr_in sa; @@ -629,6 +631,8 @@ TEST(CEPoll, LateListenerReady) srt_close(sock); srt_close(server_sock); srt_close(caller_sock); + + EXPECT_EQ(srt_cleanup(), 0); } From 496e6a74338e8ac16fad261aeaa5343d3e16f4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Sep 2022 17:25:14 +0200 Subject: [PATCH 017/174] Exported fragment to a separate function --- srtcore/core.cpp | 517 +++++++++++++++++++++++++++-------------------- srtcore/core.h | 6 + 2 files changed, 303 insertions(+), 220 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 93224f418..1972d57c5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9829,20 +9829,10 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return true; } -int srt::CUDT::processData(CUnit* in_unit) +int srt::CUDT::checkLazySpawnLatencyThread() { - if (m_bClosing) - return -1; - - CPacket &packet = in_unit->m_Packet; - - // Just heard from the peer, reset the expiration count. - m_iEXPCount = 1; - m_tsLastRspTime.store(steady_clock::now()); - const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; - // We are receiving data, start tsbpd thread if TsbPd is enabled if (need_tsbpd && !m_RcvTsbPdThread.joinable()) { ScopedLock lock(m_RcvTsbPdStartupLock); @@ -9869,9 +9859,264 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + return 0; +} + +CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +{ + return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); +} + +static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; + + +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +{ + int initial_loss_ttl = 0; + if (m_bPeerRexmitFlag) + initial_loss_ttl = m_iReorderTolerance; + + bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added + w_reorder_prevent_lossreport = false; + + // Loop over all incoming packets that were filtered out. + // In case when there is no filter, there's just one packet in 'incoming', + // the one that came in the input of this function. + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + { + CUnit * u = *unitIt; + CPacket &rpkt = u->m_Packet; + const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; + + time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); + IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + + int buffer_add_result; + bool adding_successful = true; + + // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. + // This is the offset in the buffer; if this is negative, it means that + // this sequence is already in the past and the buffer is not interested. + // Meaning, this packet will be rejected, even if it could potentially be + // one of missing packets in the transmission. + int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); + + IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); + + if (offset < 0) + { + IF_HEAVY_LOGGING(exc_type = "BELATED"); + enterCS(m_StatsLock); + const double bltime = (double) CountIIR( + uint64_t(m_stats.traceBelatedTime) * 1000, + count_microseconds(steady_clock::now() - pts), 0.2); + + m_stats.traceBelatedTime = bltime / 1000.0; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); + leaveCS(m_StatsLock); + HLOGC(qrlog.Debug, + log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset << " (BELATED/" + << rexmitstat[pktrexmitflag] << ") FLAGS: " << rpkt.MessageFlagStr()); + continue; + } + + int avail_bufsize = 0; // needed in logging + + // This is executed only when bonding is enabled and only + // with the new buffer (in which case the buffer is in the group). + + avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); + if (offset >= avail_bufsize) + { + // This is already a sequence discrepancy. Probably there could be found + // some way to make it continue reception by overriding the sequence and + // make a kinda TLKPTDROP, but there has been found no reliable way to do this. + if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) + { + // Only in live mode. In File mode this shall not be possible + // because the sender should stop sending in this situation. + // In Live mode this means that there is a gap between the + // lowest sequence in the empty buffer and the incoming sequence + // that exceeds the buffer size. Receiving data in this situation + // is no longer possible and this is a point of no return. + + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + + return -2; + } + else + { +#if ENABLE_NEW_RCVBUFFER + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); +#else + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(steady_clock::now()) + ); +#endif + + return -1; + } + } + +#if ENABLE_NEW_RCVBUFFER + buffer_add_result = m_pRcvBuffer->insert(u); +#else + buffer_add_result = m_pRcvBuffer->addData(u, offset); +#endif + if (buffer_add_result < 0) + { + // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. + // So this packet is "redundant". + IF_HEAVY_LOGGING(exc_type = "UNACKED"); + adding_successful = false; + } + else + { + w_new_inserted = true; + + IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); + excessive = false; + if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) + { + EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + if (rc != ENCS_CLEAR) + { + // Heavy log message because if seen once the message may happen very often. + HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); + adding_successful = false; + IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + ScopedLock lg(m_StatsLock); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + } + } + } + + if (adding_successful) + { + ScopedLock statslock(m_StatsLock); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); + } + +#if ENABLE_HEAVY_LOGGING + std::ostringstream expectspec; + if (excessive) + expectspec << "EXCESSIVE(" << exc_type << ")"; + else + expectspec << "ACCEPTED"; + + std::ostringstream bufinfo; + + if (m_pRcvBuffer) + { + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) + << ")"; + } + + // Empty buffer info in case of groupwise receiver. + // There's no way to obtain this information here. + + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo + << " offset=" << offset + << bufinfo.str() + << " RSL=" << expectspec.str() + << " SN=" << rexmitstat[pktrexmitflag] + << " FLAGS: " + << rpkt.MessageFlagStr()); +#endif + + // Decryption should have made the crypto flags EK_NOENC. + // Otherwise it's an error. + if (adding_successful) + { + HLOGC(qrlog.Debug, + log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + + // Lame way to make an optional variable. + bool have_loss = false; + + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + { + int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + + have_loss = true; + w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); + HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); + } + + if (initial_loss_ttl && have_loss) + { + // pack loss list for (possibly belated) NAK + // The LOSSREPORT will be sent in a while. + + ScopedLock lg(m_RcvLossLock); + for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) + { + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + HLOGC(qrlog.Debug, + log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second + << ") tolerance: " << initial_loss_ttl); + + } + w_reorder_prevent_lossreport = true; + } + } + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + // + // Group note: for the new group receiver the group hosts the receiver + // buffer, but the socket still maintains the losses. + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + { + m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + } + else + { + unlose(rpkt); // was BELATED or RETRANSMITTED + w_was_sent_in_order &= 0 != pktrexmitflag; + } + } + + return 0; +} + +int srt::CUDT::processData(CUnit* in_unit) +{ + if (m_bClosing) + return -1; + + CPacket &packet = in_unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + m_tsLastRspTime.store(steady_clock::now()); + + + // We are receiving data, start tsbpd thread if TsbPd is enabled + if (-1 == checkLazySpawnLatencyThread()) + { + return -1; + } + const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2; #if ENABLE_HEAVY_LOGGING - static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; string rexmit_reason; #endif @@ -10032,223 +10277,55 @@ int srt::CUDT::processData(CUnit* in_unit) } #endif + // NULL time by default + time_point next_tsbpd_avail; + bool new_inserted = false; + + if (m_PacketFilter) + { + // Stuff this data into the filter + m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); + HLOGC(qrlog.Debug, + log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + << " loss to report, " + << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" + : "REPORT ONLY THOSE")); + } + else + { + // Stuff in just one packet that has come in. + incoming.push_back(in_unit); + } + { // Start of offset protected section // Prevent TsbPd thread from modifying Ack position while adding data // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); - - // vector undec_units; - if (m_PacketFilter) - { - // Stuff this data into the filter - m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); - HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) - << " loss to report, " - << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" - : "REPORT ONLY THOSE")); - } - else - { - // Stuff in just one packet that has come in. - incoming.push_back(in_unit); - } - - bool excessive = true; // stays true unless it was successfully added - // Needed for possibly check for needsQuickACK. bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0); - // Loop over all incoming packets that were filtered out. - // In case when there is no filter, there's just one packet in 'incoming', - // the one that came in the input of this function. - for (vector::iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) - { - CUnit * u = *unitIt; - CPacket &rpkt = u->m_Packet; - - // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. - // This is the offset in the buffer; if this is negative, it means that - // this sequence is already in the past and the buffer is not interested. - // Meaning, this packet will be rejected, even if it could potentially be - // one of missing packets in the transmission. - int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); - - IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); - - if (offset < 0) - { - IF_HEAVY_LOGGING(exc_type = "BELATED"); - steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - const double bltime = (double) CountIIR( - uint64_t(m_stats.traceBelatedTime) * 1000, - count_microseconds(steady_clock::now() - tsbpdtime), 0.2); - - enterCS(m_StatsLock); - m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.rcvr.recvdBelated.count(rpkt.getLength()); - leaveCS(m_StatsLock); - HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" - << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr()); - continue; - } - - const int avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); - if (offset >= avail_bufsize) - { - // This is already a sequence discrepancy. Probably there could be found - // some way to make it continue reception by overriding the sequence and - // make a kinda TLKPTDROP, but there has been found no reliable way to do this. - if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) - { - // Only in live mode. In File mode this shall not be possible - // because the sender should stop sending in this situation. - // In Live mode this means that there is a gap between the - // lowest sequence in the empty buffer and the incoming sequence - // that exceeds the buffer size. Receiving data in this situation - // is no longer possible and this is a point of no return. - - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); - - // This is a scoped lock with AckLock, but for the moment - // when processClose() is called this lock must be taken out, - // otherwise this will cause a deadlock. We don't need this - // lock anymore, and at 'return' it will be unlocked anyway. - recvbuf_acklock.unlock(); - processClose(); - return -1; - } - else - { -#if ENABLE_NEW_RCVBUFFER - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) - ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif - - return -1; - } - } + int res = handleSocketPacketReception(incoming, + (new_inserted), + (was_sent_in_order), + (reorder_prevent_lossreport), + (srt_loss_seqs)); - bool adding_successful = true; -#if ENABLE_NEW_RCVBUFFER - if (m_pRcvBuffer->insert(u) < 0) -#else - if (m_pRcvBuffer->addData(u, offset) < 0) -#endif - { - // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. - // So this packet is "redundant". - IF_HEAVY_LOGGING(exc_type = "UNACKED"); - adding_successful = false; - } - else - { - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); - excessive = false; - if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; - if (rc != ENCS_CLEAR) - { - // Heavy log message because if seen once the message may happen very often. - HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); - adding_successful = false; - IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); - - ScopedLock lg(m_StatsLock); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); - } - } - } - - if (adding_successful) - { - ScopedLock statslock(m_StatsLock); - m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); - } - -#if ENABLE_HEAVY_LOGGING - std::ostringstream expectspec; - if (excessive) - expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; - else - expectspec << "ACCEPTED"; - - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo - << " offset=" << offset - << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) - << ") " - << " RSL=" << expectspec.str() - << " SN=" << rexmitstat[pktrexmitflag] - << " FLAGS: " - << rpkt.MessageFlagStr()); -#endif - - // Decryption should have made the crypto flags EK_NOENC. - // Otherwise it's an error. - if (adding_successful) - { - // XXX move this code do CUDT::defaultPacketArrival and call it from here: - // srt_loss_seqs = CALLBACK_CALL(m_cbPacketArrival, rpkt); - - HLOGC(qrlog.Debug, - log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. - { - int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - - srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. + if (res == -2) + { + // This is a scoped lock with AckLock, but for the moment + // when processClose() is called this lock must be taken out, + // otherwise this will cause a deadlock. We don't need this + // lock anymore, and at 'return' it will be unlocked anyway. + recvbuf_acklock.unlock(); + processClose(); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - } - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs) - << " tolerance: " << initial_loss_ttl); - reorder_prevent_lossreport = true; - } - } - } + return -1; + } - // Update the current largest sequence number that has been received. - // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) - { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received - } - else - { - unlose(rpkt); // was BELATED or RETRANSMITTED - was_sent_in_order &= 0 != pktrexmitflag; - } + if (res == -1) + { + return -1; } // This is moved earlier after introducing filter because it shouldn't @@ -10269,11 +10346,11 @@ int srt::CUDT::processData(CUnit* in_unit) } } - if (excessive) + if (!new_inserted) { return -1; } - } // End of recvbuf_acklock + } if (m_bClosing) { diff --git a/srtcore/core.h b/srtcore/core.h index fa58ca7c2..72946a5bf 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,6 +1062,12 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + + // Group passed here by void* because in the current imp it's + // unused and shall not be used in case when bonding is off + time_point getPacketPTS(void* grp, const CPacket& packet); + int checkLazySpawnLatencyThread(); void processClose(); /// Process the request after receiving the handshake from caller. From 03214ff8bf36706e0378335af8288211fcd92fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 30 Sep 2022 18:03:29 +0200 Subject: [PATCH 018/174] Applied changes from PR 2467-before merging --- srtcore/core.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1972d57c5..4c50d30cf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9940,14 +9940,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, + log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " + << (offset - avail_bufsize + 1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } @@ -10020,8 +10018,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() + bufinfo + << " avail=" << avail_bufsize << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) From 96e92dd558285ed484d69e2bbc611b2ff401fc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 25 Oct 2022 15:51:32 +0200 Subject: [PATCH 019/174] Adjusted to the latest changes in the group branch, per changes in master --- srtcore/core.cpp | 192 +++++++++++++++++++---------------------------- srtcore/core.h | 2 +- 2 files changed, 77 insertions(+), 117 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4c50d30cf..1355daf01 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9870,14 +9870,9 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { - int initial_loss_ttl = 0; - if (m_bPeerRexmitFlag) - initial_loss_ttl = m_iReorderTolerance; - bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added - w_reorder_prevent_lossreport = false; // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', @@ -9940,38 +9935,29 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, - log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " - << (offset - avail_bufsize + 1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } else { -#if ENABLE_NEW_RCVBUFFER LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo << ", insert offset " << offset << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif return -1; } } -#if ENABLE_NEW_RCVBUFFER buffer_add_result = m_pRcvBuffer->insert(u); -#else - buffer_add_result = m_pRcvBuffer->addData(u, offset); -#endif if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -10018,8 +10004,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo - << " avail=" << avail_bufsize + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) @@ -10045,35 +10031,15 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& HLOGC(qrlog.Debug, log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - // Lame way to make an optional variable. - bool have_loss = false; - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - have_loss = true; w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } - if (initial_loss_ttl && have_loss) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second - << ") tolerance: " << initial_loss_ttl); - - } - w_reorder_prevent_lossreport = true; - } } // Update the current largest sequence number that has been received. @@ -10189,7 +10155,6 @@ int srt::CUDT::processData(CUnit* in_unit) loss_seqs_t srt_loss_seqs; vector incoming; bool was_sent_in_order = true; - bool reorder_prevent_lossreport = false; // If the peer doesn't understand REXMIT flag, send rexmit request // always immediately. @@ -10197,37 +10162,26 @@ int srt::CUDT::processData(CUnit* in_unit) if (m_bPeerRexmitFlag) initial_loss_ttl = m_iReorderTolerance; - // After introduction of packet filtering, the "recordable loss detection" - // does not exactly match the true loss detection. When a FEC filter is - // working, for example, then getting one group filled with all packet but - // the last one and the FEC control packet, in this special case this packet - // won't be notified at all as lost because it will be recovered by the - // filter immediately before anyone notices what happened (and the loss - // detection for the further functionality is checked only afterwards, - // and in this case the immediate recovery makes the loss to not be noticed - // at all). - // - // Because of that the check for losses must happen BEFORE passing the packet - // to the filter and before the filter could recover the packet before anyone - // notices :) - - if (packet.getMsgSeq() != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing - { - int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); - // Difference between these two sequence numbers is expected to be: - // 0 - duplicated last packet (theory only) - // 1 - subsequent packet (alright) - // <0 - belated or recovered packet - // >1 - jump over a packet loss (loss = seqdiff-1) + // Track packet loss in statistics early, because a packet filter (e.g. FEC) might recover it later on, + // supply the missing packet(s), and the loss will no longer be visible for the code that follows. + if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing + { + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + // Difference between these two sequence numbers is expected to be: + // 0 - duplicated last packet (theory only) + // 1 - subsequent packet (alright) + // <0 - belated or recovered packet + // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { const int loss = diff - 1; // loss is all that is above diff == 1 + ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, - log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " + log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); } @@ -10256,20 +10210,21 @@ int srt::CUDT::processData(CUnit* in_unit) // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long // as gi is non-NULL iterator, the group does exist and it does contain - // this socket as member (that is, 'gi' cannot be a dangling iterator). + // this socket as member (that is, 'gi' cannot be a dangling pointer). if (gi != NULL) { if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, - log << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] + log << CONID() << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] << " -> RUNNING."); gi->rcvstate = SRT_GST_RUNNING; } else { - HLOGC(qrlog.Debug, log << "processData: IN-GROUP rcv state transition NOT DONE - state:" - << srt_log_grp_state[gi->rcvstate]); + HLOGC(qrlog.Debug, + log << CONID() << "processData: IN-GROUP rcv state transition NOT DONE - state:" + << srt_log_grp_state[gi->rcvstate]); } } } @@ -10284,7 +10239,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Stuff this data into the filter m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + log << CONID() << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) << " loss to report, " << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" : "REPORT ONLY THOSE")); @@ -10306,7 +10261,6 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), (was_sent_in_order), - (reorder_prevent_lossreport), (srt_loss_seqs)); if (res == -2) @@ -10326,6 +10280,25 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + if (!srt_loss_seqs.empty()) + { + ScopedLock lock(m_RcvLossLock); + + HLOGC(qrlog.Debug, + log << CONID() << "processData: RECORDING LOSS: " << Printable(srt_loss_seqs) + << " tolerance=" << initial_loss_ttl); + + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) + { + m_pRcvLossList->insert(i->first, i->second); + if (initial_loss_ttl) + { + // The LOSSREPORT will be sent after initial_loss_ttl. + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + } + } + } + // This is moved earlier after introducing filter because it shouldn't // be executed in case when the packet was rejected by the receiver buffer. // However now the 'excessive' condition may be true also in case when @@ -10370,37 +10343,21 @@ int srt::CUDT::processData(CUnit* in_unit) if (!srt_loss_seqs.empty()) { - // A loss is detected - { - // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)? - // And probably protect m_FreshLoss as well. - - HLOGC(qrlog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING."); - // if record_loss == false, nothing will be contained here - // Insert lost sequence numbers to the receiver loss list - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - // If loss found, insert them to the receiver loss list - m_pRcvLossList->insert(i->first, i->second); - } - } - const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS; - if (!reorder_prevent_lossreport && report_recorded_loss) + if (!initial_loss_ttl && report_recorded_loss) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } else { - HLOGC(qrlog.Debug, log << "loss: socket is not TSBPD, not signaling"); + HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } } @@ -10411,12 +10368,12 @@ int srt::CUDT::processData(CUnit* in_unit) // With NEVER, nothing is to be reported. if (!filter_loss_seqs.empty()) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } } @@ -10781,7 +10738,7 @@ void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) ScopedLock lg(m_RcvLossLock); m_pRcvLossList->remove(from, to); - HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqoff(from, to)); + HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqlen(from, to)); if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) return; @@ -10883,12 +10840,12 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] - HLOGC(cnlog.Debug, log << "processConnectRequest: received a connection request"); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); if (m_bClosing) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because closing."); return m_RejectReason; } @@ -10900,7 +10857,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (m_bBroken) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because broken."); return m_RejectReason; } // When CHandShake::m_iContentSize is used in log, the file fails to link! @@ -10916,8 +10873,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { m_RejectReason = SRT_REJ_ROGUE; HLOGC(cnlog.Debug, - log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len - << ")"); + log << CONID() << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() + << " (expected: " << exp_len << ")"); return m_RejectReason; } @@ -10927,7 +10884,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!packet.isControl(UMSG_HANDSHAKE)) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message"); + LOGC(cnlog.Error, + log << CONID() << "processConnectRequest: the packet received as handshake is not a handshake message"); return m_RejectReason; } @@ -10945,14 +10903,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) int32_t cookie_val = bake(addr); - HLOGC(cnlog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as // RESPONSE:INDUCTION. if (hs.m_iReqType == URQ_INDUCTION) { - HLOGC(cnlog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket"); + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: received type=induction, sending back with cookie+socket"); // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies @@ -10981,7 +10940,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen); bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, - log << "processConnectRequest: " << (whether ? "" : "NOT ") + log << CONID() << "processConnectRequest: " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); size_t size = packet.getLength(); @@ -10989,7 +10948,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) setPacketTS(packet, steady_clock::now()); // Display the HS before sending it to peer - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (i): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (i): " << hs.show()); m_pSndQueue->sendto(addr, packet); return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code. @@ -11002,13 +10961,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!hs.valid()) { - LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); + LOGC(cnlog.Error, log << CONID() << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); m_RejectReason = SRT_REJ_ROGUE; return SRT_REJ_ROGUE; } HLOGC(cnlog.Debug, - log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); + log << CONID() << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) + << " - checking cookie..."); if (hs.m_iCookie != cookie_val) { cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie @@ -11016,15 +10976,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iCookie != cookie_val) { m_RejectReason = SRT_REJ_RDVCOOKIE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); return m_RejectReason; } - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); } else { - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } int32_t id = hs.m_iID; @@ -11069,15 +11029,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason - << " MSG: " << srt_rejectreason_str(m_RejectReason)); + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.m_iID = id; setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (e): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } else diff --git a/srtcore/core.h b/srtcore/core.h index 72946a5bf..da113f93f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,7 +1062,7 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // Group passed here by void* because in the current imp it's // unused and shall not be used in case when bonding is off From 89e927f99daa1371289c77bd835bfc0c8c5726bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 26 Oct 2022 17:17:58 +0200 Subject: [PATCH 020/174] Added lacking fix for AEAD --- srtcore/core.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 72fee0e45..048d3f903 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9817,7 +9817,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + // TODO: reset and restore the timestamp if TSBPD is disabled. + // Reset retransmission flag (must be excluded from GCM auth tag). + u->m_Packet.setRexmitFlag(false); + const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag. + if (rc != ENCS_CLEAR) { // Heavy log message because if seen once the message may happen very often. @@ -9825,6 +9830,21 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& adding_successful = false; IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + // Drop a packet from the receiver buffer. + // Dropping depends on the configuration mode. If message mode is enabled, we have to drop the whole message. + // Otherwise just drop the exact packet. + if (m_config.bMessageAPI) + m_pRcvBuffer->dropMessage(SRT_SEQNO_NONE, SRT_SEQNO_NONE, u->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); + else + m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE); + + LOGC(qrlog.Error, log << CONID() << "AEAD decryption failed, breaking the connection."); + m_bBroken = true; + m_iBrokenCounter = 0; + } + ScopedLock lg(m_StatsLock); m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); } From a56c84d5b98e3b3e96d1aea6b4d3623bab997abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 27 Oct 2022 16:05:29 +0200 Subject: [PATCH 021/174] Fixed: block static variable that might be unused when no logging (travis report) --- srtcore/core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 048d3f903..42ac4aab9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9711,8 +9711,7 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } -static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; - +SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { From 0168c5ea583ffd2963bac90e91cbfcb97390f297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2022 15:06:33 +0100 Subject: [PATCH 022/174] Fixed after PR comments --- srtcore/core.cpp | 4 ++-- srtcore/core.h | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 42ac4aab9..6d12656fd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9728,7 +9728,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const bool retransmitted = pktrexmitflag == 1; time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); int buffer_add_result; bool adding_successful = true; diff --git a/srtcore/core.h b/srtcore/core.h index 93674cb3b..8cbf47650 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1053,11 +1053,29 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + + /// This function passes the incoming packet to the initial processing + /// (like packet filter) and is about to store it effectively to the + /// receiver buffer and do some postprocessing (decryption) if necessary + /// and report the status thereof. + /// + /// @param incoming [in] The packet coming from the network medium + /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. + /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. + /// + /// @return 0 The call was successful (regardless if the packet was accepted or not). + /// @return -1 The call has failed: no space left in the buffer. + /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - // Group passed here by void* because in the current imp it's - // unused and shall not be used in case when bonding is off - time_point getPacketPTS(void* grp, const CPacket& packet); + // This function is to return the packet's play time (time when + // it is submitted to the reading application) of the given packet. + // This grp passed here by void* because in the current imp it's + // unused and shall not be used in case when ENABLE_BONDING=0. + time_point getPacketPlayTime(void* grp, const CPacket& packet); + + /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); void processClose(); From 870509f7e2b1a5ff72eb4520912ce25058d8ddf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Nov 2022 12:08:19 +0100 Subject: [PATCH 023/174] Renamed getPacketPTS. Moved call to obtain PTS under belated condition --- srtcore/core.cpp | 7 +++---- srtcore/core.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6d12656fd..37a13dc94 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9727,9 +9727,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; const bool retransmitted = pktrexmitflag == 1; - time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); - int buffer_add_result; bool adding_successful = true; @@ -9745,6 +9742,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (offset < 0) { IF_HEAVY_LOGGING(exc_type = "BELATED"); + time_point pts = getPktTsbPdTime(NULL, rpkt); + enterCS(m_StatsLock); const double bltime = (double) CountIIR( uint64_t(m_stats.traceBelatedTime) * 1000, diff --git a/srtcore/core.h b/srtcore/core.h index 8cbf47650..81979bb83 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1073,7 +1073,7 @@ class CUDT // it is submitted to the reading application) of the given packet. // This grp passed here by void* because in the current imp it's // unused and shall not be used in case when ENABLE_BONDING=0. - time_point getPacketPlayTime(void* grp, const CPacket& packet); + time_point getPktTsbPdTime(void* grp, const CPacket& packet); /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); From 652e35da0e7a9702496f7ef9fa95049b9e13c5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 7 Nov 2022 16:56:33 +0100 Subject: [PATCH 024/174] Applied changes for improved TSBPD and receiver buffer --- srtcore/buffer_rcv.cpp | 637 ++++++++++++++++++++++++++++++++------- srtcore/buffer_rcv.h | 301 +++++++++++++++--- srtcore/core.cpp | 264 +++++++++------- srtcore/core.h | 6 +- test/test_buffer_rcv.cpp | 5 +- 5 files changed, 952 insertions(+), 261 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7bfb00ad8..ede2033cb 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -98,7 +98,7 @@ namespace { * RcvBufferNew (circular buffer): * * |<------------------- m_iSize ----------------------------->| - * | |<----------- m_iMaxPosInc ------------>| | + * | |<----------- m_iMaxPosOff ------------>| | * | | | | * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] @@ -112,20 +112,22 @@ namespace { * thread safety: * m_iStartPos: CUDT::m_RecvLock * m_iLastAckPos: CUDT::m_AckLock - * m_iMaxPosInc: none? (modified on add and ack + * m_iMaxPosOff: none? (modified on add and ack */ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) - , m_iStartSeqNo(initSeqNo) + , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. , m_iStartPos(0) + , m_iEndPos(0) + , m_iDropPos(0) , m_iFirstNonreadPos(0) - , m_iMaxPosInc(0) + , m_iMaxPosOff(0) , m_iNotch(0) - , m_numOutOfOrderPackets(0) - , m_iFirstReadableOutOfOrder(-1) + , m_numRandomPackets(0) + , m_iFirstRandomMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -137,7 +139,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b CRcvBuffer::~CRcvBuffer() { - // Can be optimized by only iterating m_iMaxPosInc from m_iStartPos. + // Can be optimized by only iterating m_iMaxPosOff from m_iStartPos. for (FixedArray::iterator it = m_entries.begin(); it != m_entries.end(); ++it) { if (!it->pUnit) @@ -148,7 +150,13 @@ CRcvBuffer::~CRcvBuffer() } } -int CRcvBuffer::insert(CUnit* unit) +void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) +{ + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos + << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); +} + +CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); @@ -159,53 +167,264 @@ int CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + int32_t avail_seq; + int avail_range; + if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); - return -2; + return InsertInfo(InsertInfo::BELATED); } + IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); if (offset >= (int)capacity()) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - return -3; + + // Calculation done for the sake of possible discrepancy + // in order to inform the caller what to do. + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_seq = packetAt(m_iStartPos).getSeqNo(); + avail_range = m_iEndPos - m_iStartPos; + } + else if (m_iDropPos == m_iEndPos) + { + avail_seq = SRT_SEQNO_NONE; + avail_range = 0; + } + else + { + avail_seq = packetAt(m_iDropPos).getSeqNo(); + + // We don't know how many packets follow it exactly, + // but in this case it doesn't matter. We know that + // at least one is there. + avail_range = 1; + } + + IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); + + return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); } // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - const int pos = (m_iStartPos + offset) % m_szSize; - if (offset >= m_iMaxPosInc) - m_iMaxPosInc = offset + 1; + const int newpktpos = incPos(m_iStartPos, offset); + const int prev_max_off = m_iMaxPosOff; + bool extended_end = false; + if (offset >= m_iMaxPosOff) + { + m_iMaxPosOff = offset + 1; + extended_end = true; + } // Packet already exists - SRT_ASSERT(pos >= 0 && pos < int(m_szSize)); - if (m_entries[pos].status != EntryState_Empty) + // (NOTE: the above extension of m_iMaxPosOff is + // possible even before checking that the packet + // exists because existence of a packet beyond + // the current max position is not possible). + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); - return -1; + IF_HEAVY_LOGGING(debugShowState((debug_source + " redundant").c_str())); + return InsertInfo(InsertInfo::REDUNDANT); } - SRT_ASSERT(m_entries[pos].pUnit == NULL); + SRT_ASSERT(m_entries[newpktpos].pUnit == NULL); m_pUnitQueue->makeUnitTaken(unit); - m_entries[pos].pUnit = unit; - m_entries[pos].status = EntryState_Avail; + m_entries[newpktpos].pUnit = unit; + m_entries[newpktpos].status = EntryState_Avail; countBytes(1, (int)unit->m_Packet.getLength()); + // Set to a value, if due to insertion there was added + // a packet that is earlier to be retrieved than the earliest + // currently available packet. + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numOutOfOrderPackets; - onInsertNotInOrderPacket(pos); + ++m_numRandomPackets; + onInsertNotInOrderPacket(newpktpos); } updateNonreadPos(); + + CPacket* avail_packet = NULL; + + if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_packet = &packetAt(m_iStartPos); + avail_range = m_iEndPos - m_iStartPos; + } + else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_range = 1; + } + else if (m_iDropPos != m_iEndPos) + { + avail_packet = &packetAt(m_iDropPos); + avail_range = 1; + } + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); - return 0; + IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); + + if (avail_packet) + return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); + else + return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } +// This function should be called after having m_iEndPos +// has somehow be set to position of a non-empty cell. +// This can happen by two reasons: +// - the cell has been filled by incoming packet +// - the value has been reset due to shifted m_iStartPos +// This means that you have to search for a new gap and +// update the m_iEndPos and m_iDropPos fields, or set them +// both to the end of range. +// +// prev_max_pos should be the position represented by m_iMaxPosOff. +// Passed because it is already calculated in insert(), otherwise +// it would have to be calculated here again. +void CRcvBuffer::updateGapInfo(int prev_max_pos) +{ + int pos = m_iEndPos; + + // First, search for the next gap, max until m_iMaxPosOff. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status == EntryState_Empty) + { + break; + } + } + if (pos == prev_max_pos) + { + // Reached the end and found no gaps. + m_iEndPos = prev_max_pos; + m_iDropPos = prev_max_pos; + } + else + { + // Found a gap at pos + m_iEndPos = pos; + m_iDropPos = pos; // fallback, although SHOULD be impossible + // So, search for the first position to drop up to. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status != EntryState_Empty) + { + m_iDropPos = pos; + break; + } + } + } +} + +/// Request to remove from the receiver buffer +/// all packets with earlier sequence than @a seqno. +/// (Meaning, the packet with given sequence shall +/// be the first packet in the buffer after the operation). int CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -218,9 +437,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosInc -= len; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + m_iMaxPosOff -= len; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; const int iDropCnt = len; while (len > 0) @@ -235,13 +454,21 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Update positions m_iStartSeqNo = seqno; // Move forward if there are "read/drop" entries. + // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartPos; + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); + + IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; } @@ -250,7 +477,7 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosInc); + const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); return dropUpTo(end_seqno); } @@ -259,7 +486,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); // TODO: count bytes as removed? - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); @@ -286,6 +513,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) // Check if units before m_iFirstNonreadPos are dropped. bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartSeqNo; + updateGapInfo(end_pos); + if (needUpdateNonreadPos) { m_iFirstNonreadPos = m_iStartPos; @@ -293,10 +525,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } @@ -341,24 +574,47 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } -int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const +{ + if (m_iStartPos == m_iEndPos) + { + // Initial contiguous region empty (including empty buffer). + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); + w_seq = m_iStartSeqNo; + return m_iMaxPosOff > 0; + } + + int end_off = offPos(m_iStartPos, m_iEndPos); + + w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + << " end=%" << w_seq); + + return (end_off < m_iMaxPosOff); +} + +int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + if (!canReadInOrder && m_iFirstRandomMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); @@ -367,7 +623,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) char* dst = data; int pkts_read = 0; int bytes_extracted = 0; // The total number of bytes extracted from the buffer. - const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed + + int32_t out_seqlo = SRT_SEQNO_NONE; + int32_t out_seqhi = SRT_SEQNO_NONE; + for (int i = readPos;; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -381,6 +640,11 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); + if (out_seqlo == SRT_SEQNO_NONE) + out_seqlo = pktseqno; + + out_seqhi = pktseqno; + // unitsize can be zero const size_t unitsize = std::min(remain, pktsize); memcpy(dst, packet.m_pcData, unitsize); @@ -393,8 +657,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (m_tsbpd.isEnabled()) updateTsbPdTimeBase(packet.getMsgTimeStamp()); - if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) - --m_numOutOfOrderPackets; + if (m_numRandomPackets && !packet.getMsgOrderFlag()) + --m_numRandomPackets; const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) @@ -409,11 +673,26 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) msgctrl->pktseq = pktseqno; releaseUnitInPos(i); - if (updateStartPos) + if (isReadingFromStart) { m_iStartPos = incPos(i); - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + + // m_iEndPos and m_iDropPos should be + // equal to m_iStartPos only if the buffer + // is empty - but in this case the extraction will + // not be done. Otherwise m_iEndPos should + // point to the first empty cell, and m_iDropPos + // point to the first busy cell after a gap, or + // at worst be equal to m_iEndPos. + + // Therefore none of them should be updated + // because they should be constantly updated + // on an incoming packet, while this function + // should not read further than to the first + // empty cell at worst. + + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(pktseqno); } else @@ -424,8 +703,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (pbLast) { - if (readPos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + if (readPos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; break; } } @@ -434,16 +713,59 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) releaseNextFillerEntries(); - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } + // Now that we have m_iStartPos potentially shifted, reinitialize + // m_iEndPos and m_iDropPos. + + int pend_pos = incPos(m_iStartPos, m_iMaxPosOff); + + // First check: is anything in the beginning + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + // If so, shift m_iEndPos up to the first nonexistent unit + // XXX Try to optimize search by splitting into two loops if necessary. + + m_iEndPos = incPos(m_iStartPos); + while (m_entries[m_iEndPos].status == EntryState_Avail) + { + m_iEndPos = incPos(m_iEndPos); + if (m_iEndPos == pend_pos) + break; + } + + // If we had first packet available, then there's also no drop pos. + m_iDropPos = m_iEndPos; + + } + else + { + // If not, reset m_iEndPos and search for the first after-drop candidate. + m_iEndPos = m_iStartPos; + m_iDropPos = m_iEndPos; + + while (m_entries[m_iDropPos].status != EntryState_Avail) + { + m_iDropPos = incPos(m_iDropPos); + if (m_iDropPos == pend_pos) + { + // Nothing found - set drop pos equal to end pos, + // which means there's no drop + m_iDropPos = m_iEndPos; + break; + } + } + } + + if (!m_tsbpd.isEnabled()) - // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // We need updateFirstReadableRandom() here even if we are reading inorder, // incase readable inorder packets are all read out. - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); const int bytes_read = int(dst - data); if (bytes_read < bytes_extracted) @@ -453,6 +775,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + if (pw_seqrange) + *pw_seqrange = make_pair(out_seqlo, out_seqhi); + + IF_HEAVY_LOGGING(debugShowState("readmsg")); return bytes_read; } @@ -530,8 +856,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iNotch = 0; m_iStartPos = p; - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); } else @@ -547,7 +873,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -557,6 +883,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } + IF_HEAVY_LOGGING(debugShowState("readbuf")); return iBytesRead; } @@ -572,15 +899,12 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + return hasReadableInorderPkts() || (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const { - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - - return int(m_szSize + m_iFirstNonreadPos - m_iStartPos); + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -588,10 +912,10 @@ int CRcvBuffer::getTimespan_ms() const if (!m_tsbpd.isEnabled()) return 0; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return 0; - const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); // Should not happen if TSBPD is enabled (reading out of order is not allowed). SRT_ASSERT(m_entries[lastpos].pUnit != NULL); if (m_entries[lastpos].pUnit == NULL) @@ -631,35 +955,28 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); - for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + // Check the state of the very first packet first + if (m_entries[m_iStartPos].status == EntryState_Avail) { - // TODO: Maybe check status? - if (!m_entries[i].pUnit) - continue; - - const CPacket& packet = m_entries[i].pUnit->m_Packet; - const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; - return info; + SRT_ASSERT(m_entries[m_iStartPos].pUnit); + return (PacketInfo) { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + } + // If not, get the information from the drop + if (m_iDropPos != m_iEndPos) + { + const CPacket& pkt = packetAt(m_iDropPos); + return (PacketInfo) { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; } - const PacketInfo info = { -1, false, time_point() }; - return info; + return (PacketInfo) { SRT_SEQNO_NONE, false, time_point() }; } std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable()); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); return std::pair(m_iStartSeqNo, seqno_last); } -size_t CRcvBuffer::countReadable() const -{ - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - return m_szSize + m_iFirstNonreadPos - m_iStartPos; -} - bool CRcvBuffer::isRcvDataReady(time_point time_now) const { const bool haveInorderPackets = hasReadableInorderPkts(); @@ -668,8 +985,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } if (!haveInorderPackets) @@ -693,11 +1010,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstReadableOutOfOrder >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + if (m_iFirstRandomMsgPos >= 0) { - SRT_ASSERT(m_numOutOfOrderPackets > 0); - const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + SRT_ASSERT(m_numRandomPackets > 0); + const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -742,9 +1059,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) { - --m_numOutOfOrderPackets; - if (pos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + --m_numRandomPackets; + if (pos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -759,24 +1076,24 @@ void CRcvBuffer::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; - --m_iMaxPosInc; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + --m_iMaxPosOff; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; } } // TODO: Is this function complete? There are some comments left inside. void CRcvBuffer::updateNonreadPos() { - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; for (int i = pos; i != end_pos; i = incPos(i)) @@ -786,8 +1103,12 @@ void CRcvBuffer::updateNonreadPos() break; } + // m_iFirstNonreadPos is moved to the first position BEHIND + // the PB_LAST packet of the message. There's no guaratnee that + // the cell at this position isn't empty. + // Check PB_LAST only in message mode. - if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -818,7 +1139,7 @@ int CRcvBuffer::findLastMessagePkt() void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) { - if (m_numOutOfOrderPackets == 0) + if (m_numRandomPackets == 0) return; // If the following condition is true, there is already a packet, @@ -827,20 +1148,20 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstReadableOutOfOrder >= 0) + if (m_iFirstRandomMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. // So the should be unacknowledged packets. - SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_iMaxPosOff > 0); SRT_ASSERT(m_entries[insertPos].pUnit); - const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const CPacket& pkt = packetAt(insertPos); const PacketBoundary boundary = pkt.getMsgBoundary(); //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstReadableOutOfOrder = insertPos; + // m_iFirstRandomMsgPos = insertPos; // return; //} @@ -856,18 +1177,18 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) if (firstPktPos < 0) return; - m_iFirstReadableOutOfOrder = firstPktPos; + m_iFirstRandomMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableOutOfOrder() +bool CRcvBuffer::checkFirstReadableRandom() { - if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -888,20 +1209,20 @@ bool CRcvBuffer::checkFirstReadableOutOfOrder() return false; } -void CRcvBuffer::updateFirstReadableOutOfOrder() +void CRcvBuffer::updateFirstReadableRandom() { - if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) return; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numOutOfOrderPackets; + int outOfOrderPktsRemain = (int) m_numRandomPackets; // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; int posFirst = -1; int posLast = -1; @@ -940,7 +1261,7 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() if (boundary & PB_LAST) { - m_iFirstReadableOutOfOrder = posFirst; + m_iFirstRandomMsgPos = posFirst; return; } @@ -955,7 +1276,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; if (startPos == lastPos) return -1; @@ -997,7 +1318,7 @@ int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) return -1; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1055,19 +1376,19 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosInc << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; - if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + if (m_tsbpd.isEnabled() && m_iMaxPosOff > 0) { const PacketInfo nextValidPkt = getFirstValidPacketInfo(); ss << "(TSBPD ready in "; if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1116,4 +1437,92 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) m_mavg.update(now, pkts, bytes, timespan_ms); } +int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) +{ + int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + + // Check if it's still inside the buffer + if (offset < 0 || offset >= m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + return SRT_SEQNO_NONE; + } + + // Start position + int pos = incPos(m_iStartPos, offset); + + // Ok; likely we should stand at the m_iEndPos position. + // If this given position is earlier than this, then + // m_iEnd stands on the first loss, unless it's equal + // to the position pointed by m_iMaxPosOff. + + int32_t ret_seq = SRT_SEQNO_NONE; + int ret_off = m_iMaxPosOff; + + int end_off = offPos(m_iStartPos, m_iEndPos); + if (pos < end_off) + { + // If m_iEndPos has such a value, then there are + // no loss packets at all. + if (end_off != m_iMaxPosOff) + { + ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_off = end_off; + } + } + else + { + // Could be strange, but just as the caller wishes: + // find the first loss since this point on + // You can't rely on m_iEndPos, you are beyond that now. + // So simply find the next hole. + + // REUSE offset as a control variable + for (; offset < m_iMaxPosOff; ++offset) + { + int pos = incPos(m_iStartPos, offset); + if (m_entries[pos].status == EntryState_Empty) + { + ret_off = offset; + ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + break; + } + } + } + + // If found no loss, just return this value and do not + // rewrite nor look for anything. + + // Also no need to search anything if only the beginning was + // being looked for. + if (ret_seq == SRT_SEQNO_NONE || !pw_end) + return ret_seq; + + // We want also the end range, so continue from where you + // stopped. + + // Start from ret_off + 1 because we know already that ret_off + // points to an empty cell. + for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + { + int pos = incPos(m_iStartPos, off); + if (m_entries[pos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + return ret_seq; + } + } + + // Fallback - this should be impossible, so issue a log. + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + + // Return this in the last resort - this could only be a situation when + // a packet has somehow disappeared, but it contains empty cells up to the + // end of buffer occupied range. This shouldn't be possible at all because + // there must be a valid packet at least at the last occupied cell. + return SRT_SEQNO_NONE; +} + + } // namespace srt diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 52e927f22..1cdce3ad8 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -20,28 +20,193 @@ namespace srt { -/* - * Circular receiver buffer. - * - * |<------------------- m_szSize ---------------------------->| - * | |<------------ m_iMaxPosInc ----------->| | - * | | | | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | | - * | \__last pkt received - * | - * \___ m_iStartPos: first message to read - * - * m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) - * - * thread safety: - * start_pos_: CUDT::m_RecvLock - * first_unack_pos_: CUDT::m_AckLock - * max_pos_inc_: none? (modified on add and ack - * first_nonread_pos_: - */ +// +// Circular receiver buffer. +// +// |<------------------- m_szSize ---------------------------->| +// | |<------------ m_iMaxPosOff ----------->| | +// | | | | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | | | | +// | | | \__last pkt received +// | | | +// | | \___ m_iDropPos +// | | +// | \___ m_iEndPos +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// +// thread safety: +// start_pos_: CUDT::m_RecvLock +// first_unack_pos_: CUDT::m_AckLock +// max_pos_inc_: none? (modified on add and ack +// first_nonread_pos_: +// +// +// m_iStartPos: the first packet that should be read (might be empty) +// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos +// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// +// Operational rules: +// +// Initially: +// m_iStartPos = 0 +// m_iEndPos = 0 +// m_iDropPos = 0 +// +// When a packet has arrived, then depending on where it landed: +// +// 1. Position: next to the last read one and newest +// +// m_iStartPos unchanged. +// m_iEndPos shifted by 1 +// m_iDropPos = m_iEndPos +// +// 2. Position: after a loss, newest. +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if it was == m_iEndPos, set to this +// - otherwise unchanged +// +// 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if m_iDropPos == m_iEndPos, set to this +// - if m_iDropPos %> this sequence, set to this +// - otherwise unchanged +// +// 4. Position: after a loss, sealing -- seq equal to position of m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos: +// - since this position, search the first free cell +// - if reached the end of filled region (m_iMaxPosOff), stay there. +// m_iDropPos: +// - start from the value equal to m_iEndPos +// - walk at maximum to m_iMaxPosOff +// - find the first existing packet +// NOTE: +// If there are no "after gap" packets, then m_iMaxPosOff == m_iEndPos. +// If there is one existing packet, then one loss, then one packet, it +// should be that m_iEndPos = m_iStartPos %+ 1, m_iDropPos can reach +// to m_iStartPos %+ 2 position, and m_iMaxPosOff == m_iStartPos %+ 3. +// +// To wrap up: +// +// Let's say we have the following possibilities in a general scheme: +// +// +// [D] [C] [B] [A] (insertion cases) +// | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | +// +// WHEN INSERTING A NEW PACKET: +// +// If the incoming sequence maps to newpktpos that is: +// +// * newpktpos <% (start) : discard the packet and exit +// * newpktpos %> (size) : report discrepancy, discard and exit +// * newpktpos %> (start) and: +// * EXISTS: discard and exit (NOTE: could be also < (end)) +// [A]* seq == m_iMaxPosOff +// --> INC m_iMaxPosOff +// * m_iEndPos == previous m_iMaxPosOff +// * previous m_iMaxPosOff + 1 == m_iMaxPosOff +// --> m_iEndPos = m_iMaxPosOff +// --> m_iDropPos = m_iEndPos +// * otherwise (means the new packet caused a gap) +// --> m_iEndPos REMAINS UNCHANGED +// --> m_iDropPos = POSITION(m_iMaxPosOff) +// COMMENT: +// If this above condition isn't satisfied, then there are +// gaps, first at m_iEndPos, and m_iDropPos is at furthest +// equal to m_iMaxPosOff %- 1. The inserted packet is outside +// both the contiguous region and the following scratched region, +// so no updates on m_iEndPos and m_iDropPos are necessary. +// +// NOTE +// SINCE THIS PLACE seq cannot be a sequence of an existing packet, +// which means that earliest newpktpos == m_iEndPos, up to == m_iMaxPosOff -% 2. +// +// * otherwise (newpktpos <% max-pos): +// [D]* newpktpos == m_iEndPos: +// --> (search FIRST GAP and FIRST AFTER-GAP) +// --> m_iEndPos: increase until reaching m_iMaxPosOff +// * m_iEndPos <% m_iMaxPosOff: +// --> m_iDropPos = first VALID packet since m_iEndPos +% 1 +// * otherwise: +// --> m_iDropPos = m_iEndPos +// [B]* newpktpos %> m_iDropPos +// --> store, but do not update anything +// [C]* otherwise (newpktpos %> m_iEndPos && newpktpos <% m_iDropPos) +// --> store +// --> set m_iDropPos = newpktpos +// COMMENT: +// It is guaratneed that between m_iEndPos and m_iDropPos +// there is only a gap (series of empty cells). So wherever +// this packet lands, if it's next to m_iEndPos and before m_iDropPos +// it will be the only packet that violates the gap, hence this +// can be the only drop pos preceding the previous m_iDropPos. +// +// -- information returned to the caller should contain: +// 1. Whether adding to the buffer was successful. +// 2. Whether the "freshest" retrievable packet has been changed, that is: +// * in live mode, a newly added packet has earlier delivery time than one before +// * in stream mode, the newly added packet was at cell[0] +// * in message mode, if the newly added packet has: +// * completed the very first message +// * completed any message further than first that has out-of-order flag +// +// The information about a changed packet is important for the caller in +// live mode in order to notify the TSBPD thread. +// +// +// +// WHEN CHECKING A PACKET +// +// 1. Check the position at m_iStartPos. If there is a packet, +// return info at its position. +// +// 2. If position on m_iStartPos is empty, get the value of m_iDropPos. +// +// NOTE THAT: +// * if the buffer is empty, m_iDropPos == m_iStartPos and == m_iEndPos; +// note that m_iDropPos == m_iStartPos suffices to check that +// * if there is a packet in the buffer, but the first cell is empty, +// then m_iDropPos points to this packet, while m_iEndPos == m_iStartPos. +// Check then m_iStartPos == m_iEndPos to recognize it, and if then +// m_iDropPos isn't equal to them, you can read with dropping. +// * If cell[0] is valid, there could be only at worst cell[1] empty +// and cell[2] pointed by m_iDropPos. +// +// 3. In case of time-based checking for live mode, return empty packet info, +// if this packet's time is later than given time. +// +// WHEN EXTRACTING A PACKET +// +// 1. Extraction is only possible if there is a packet at cell[0]. +// 2. If there's no packet at cell[0], the application may request to +// drop up to the given packet, or drop the whole message up to +// the beginning of the next message. +// 3. In message mode, extraction can only extract a full message, so +// if there's no full message ready, nothing is extracted. +// 4. When the extraction region is defined, the m_iStartPos is shifted +// by the number of extracted packets. +// 5. If m_iEndPos <% m_iStartPos (after update), m_iEndPos should be +// set by searching from m_iStartPos up to m_iMaxPosOff for an empty cell. +// 6. m_iDropPos must be always updated. If m_iEndPos == m_iMaxPosOff, +// m_iDropPos is set to their value. Otherwise start from m_iEndPos +// and search a valid packet up to m_iMaxPosOff. +// 7. NOTE: m_iMaxPosOff is a delta, hence it must be set anew after update +// for m_iStartPos. +// class CRcvBuffer { @@ -54,6 +219,32 @@ class CRcvBuffer ~CRcvBuffer(); public: + + void debugShowState(const char* source); + + struct InsertInfo + { + enum Result { INSERTED = 0, REDUNDANT = -1, BELATED = -2, DISCREPANCY = -3 } result; + + // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. + + int first_seq; // sequence of the first available readable packet + time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. + int avail_range; + + InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, + time_point fp_time = time_point()) + : result(r), first_seq(fp_seq), first_time(fp_time), avail_range(range) + { + } + + InsertInfo() + : result(REDUNDANT), first_seq(SRT_SEQNO_NONE), avail_range(0) + { + } + + }; + /// Insert a unit into the buffer. /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) /// @@ -63,7 +254,8 @@ class CRcvBuffer /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. /// -3 if a packet is offset is ahead the buffer capacity. // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. - int insert(CUnit* unit); + InsertInfo insert(CUnit* unit); + void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -84,6 +276,17 @@ class CRcvBuffer /// @return the number of packets actually dropped. int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + /// Extract the "expected next" packet sequence. + /// Extract the past-the-end sequence for the first packet + /// that is expected to arrive next with preserving the packet order. + /// If the buffer is empty or the very first cell is lacking a packet, + /// it returns the sequence assigned to the first cell. Otherwise it + /// returns the sequence representing the first empty cell (the next + /// cell to the last received packet, if there are no loss-holes). + /// @param [out] w_seq: returns the sequence (always valid) + /// @return true if this sequence is followed by any valid packets + bool getContiguousEnd(int32_t& w_seq) const; + /// Read the whole message from one or several packets. /// /// @param [in,out] data buffer to write the message into. @@ -93,7 +296,7 @@ class CRcvBuffer /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. /// -1 on failure. - int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL, std::pair* pw_seqrange = NULL); /// Read acknowledged data into a user buffer. /// @param [in, out] dst pointer to the target user buffer. @@ -179,11 +382,11 @@ class CRcvBuffer /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; - size_t countReadable() const; + int32_t getFirstLossSeq(int32_t fromseq, int32_t* opt_end = NULL); bool empty() const { - return (m_iMaxPosInc == 0); + return (m_iMaxPosOff == 0); } /// Return buffer capacity. @@ -195,6 +398,14 @@ class CRcvBuffer return m_szSize - 1; } + /// Returns the currently used number of cells, including + /// gaps with empty cells, or in other words, the distance + /// between the initial position and the youngest received packet. + size_t size() const + { + return m_iMaxPosOff; + } + int64_t getDrift() const { return m_tsbpd.drift(); } // TODO: make thread safe? @@ -225,6 +436,18 @@ class CRcvBuffer inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + inline int cmpPos(int pos2, int pos1) const + { + // XXX maybe not the best implementation, but this keeps up to the rule + int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + m_szSize - m_iStartPos; + int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + m_szSize - m_iStartPos; + + return off2 - off1; + } + + // NOTE: Assumes that pUnit != NULL + CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); @@ -247,9 +470,9 @@ class CRcvBuffer /// Scan for availability of out of order packets. void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstReadableOutOfOrder is still readable. - bool checkFirstReadableOutOfOrder(); - void updateFirstReadableOutOfOrder(); + // Check if m_iFirstRandomMsgPos is still readable. + bool checkFirstReadableRandom(); + void updateFirstReadableRandom(); int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; @@ -303,20 +526,26 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - FixedArray m_entries; + typedef FixedArray entries_t; + entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartSeqNo; int m_iStartPos; // the head position for I/O (inclusive) + int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosInc; // the furthest data position - int m_iNotch; // the starting read point of the first unit + int m_iMaxPosOff; // the furthest data position + int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) + + size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false - size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false - int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to - // read + /// Points to the first packet of a message that has out-of-order flag + /// and is complete (all packets from first to last are in the buffer). + /// If there is no such message in the buffer, it contains -1. + int m_iFirstRandomMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -342,6 +571,8 @@ class CRcvBuffer time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; void updateTsbPdTimeBase(uint32_t usPktTimestamp); + bool isTsbPd() const { return m_tsbpd.isEnabled(); } + /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. std::string strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, const time_point& tsNow) const; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c614844dd..68824dd8e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -292,7 +292,7 @@ void srt::CUDT::construct() m_bPeerTsbPd = false; m_iPeerTsbPdDelay_ms = 0; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bWakeOnRecv = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; @@ -5254,7 +5254,7 @@ void * srt::CUDT::tsbpd(void* param) CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond); CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker()); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5272,6 +5272,12 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; + HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + if (!self->m_bTLPktDrop) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5374,7 +5380,7 @@ void * srt::CUDT::tsbpd(void* param) * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. */ - self->m_bTsbPdAckWakeup = false; + self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); @@ -5396,7 +5402,7 @@ void * srt::CUDT::tsbpd(void* param) * - Closing the connection */ HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); @@ -7782,10 +7788,28 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp m_tsLastSndTime.store(steady_clock::now()); } +bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) +{ + if (!m_pRcvBuffer) + { + LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); + return false; + } + { + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; + } + + return true; +} + int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; @@ -7800,39 +7824,17 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable] local_prevack = m_iDebugPrevLastAck; - - string reason = "first lost"; // just for "a reason" of giving particular % for ACK #endif + string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING - dropToGroupRecvBase(); -#endif - - // The TSBPD thread may change the first lost sequence record (TLPKTDROP). - // To avoid it the m_RcvBufferLock has to be acquired. - UniqueLock bufflock(m_RcvBufferLock); - - { - // If there is no loss, the ACK is the current largest sequence number plus 1; - // Otherwise it is the smallest sequence number in the receiver loss list. - ScopedLock lock(m_RcvLossLock); - // TODO: Consider the Fresh Loss list as well!!! - ack = m_pRcvLossList->getFirstLostSeq(); - } - - // We don't need to check the length prematurely, - // if length is 0, this will return SRT_SEQNO_NONE. - // If so happened, simply use the latest received pkt + 1. - if (ack == SRT_SEQNO_NONE) - { - ack = CSeqNo::incseq(m_iRcvCurrSeqNo); - IF_HEAVY_LOGGING(reason = "expected next"); - } + int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) + return nbsent; if (m_iRcvLastAckAck == ack) { HLOGC(xtlog.Debug, - log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); + log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); return nbsent; } @@ -7840,7 +7842,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number if (size == SEND_LITE_ACK) { - bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); @@ -7848,6 +7849,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } + // Lock the group existence until this function ends. This will be useful + // also on other places. +#if ENABLE_BONDING + CUDTUnited::GroupKeeper gkeeper (uglobal(), m_parent); +#endif + + // There are new received packets to acknowledge, update related information. + /* tsbpd thread may also call ackData when skipping packet so protect code */ + UniqueLock bufflock(m_RcvBufferLock); + // IF ack %> m_iRcvLastAck // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) @@ -7894,21 +7905,32 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #endif IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck); - // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, - // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which - // will signal m_RecvDataCond when there's time to play for particular - // data packet. + // Signalling m_RecvDataCond is not dane when TSBPD is on. + // This signalling is done in file mode in order to keep the + // API reader thread sleeping until there is a "bigger portion" + // of data to read. In TSBPD mode this isn't done because every + // packet has its individual delivery time and its readiness is signed + // off by the TSBPD thread. HLOGC(xtlog.Debug, log << CONID() << "ACK: clip %" << oldack << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); if (m_bTsbPd) { - /* Newly acknowledged data, signal TsbPD thread */ + /* + There's no need to update TSBPD in the wake-on-recv state + from ACK because it is being done already in the receiver thread + when a newly inserted packet caused provision of a new candidate + that could be delivered soon. Also, this flag is only used in TSBPD + mode and can be only set to true in the TSBPD thread. + + // Newly acknowledged data, signal TsbPD thread // CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdAckWakeup) + // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread + if (m_bWakeOnRecv) tslcc.notify_one(); + + */ } else { @@ -7970,7 +7992,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID()<< "sendCtrl(UMSG_ACK): IPE: curr(" << reason << ") %" << ack + << " <% last %" << m_iRcvLastAck); return nbsent; } @@ -8763,8 +8786,28 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // is currently in the ACK-waiting state, it may never exit. if (m_bTsbPd) { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - rcvtscc.notify_one(); + // XXX Likely this is not necessary because: + // 1. In the recv-waiting state, that is, when TSBPD thread + // sleeps forever, it will be woken up anyway on packet + // reception. + // 2. If there are any packets in the buffer and the initial + // packet cell is empty (in which situation any drop could + // occur), TSBPD thread is sleeping timely, until the playtime + // of the first "drop up to" packet. Dropping changes nothing here. + // 3. If the buffer is empty, there's nothing "to drop up to", so + // this function will not change anything in the buffer and so + // in the reception state as well. + // 4. If the TSBPD thread is waiting until a play-ready packet is + // retrieved by the API call (in which case it is also sleeping + // forever until it's woken up by the API call), it may remain + // stalled forever, if the application isn't reading the play-ready + // packet. But this means that the application got stalled anyway. + // TSBPD when woken up could at best state that there's still a + // play-ready packet that is still not retrieved and fall back + // to sleep (forever). + + //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); + //rcvtscc.notify_one(); } } @@ -9701,14 +9744,14 @@ CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', // the one that came in the input of this function. - for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end() && !m_bBroken; ++unitIt) { CUnit * u = *unitIt; CPacket &rpkt = u->m_Packet; @@ -9787,7 +9830,26 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } } - buffer_add_result = m_pRcvBuffer->insert(u); + CRcvBuffer::InsertInfo info = m_pRcvBuffer->insert(u); + + // Remember this value in order to CHECK if there's a need + // to request triggering TSBPD in case when TSBPD is in the + // state of waiting forever and wants to know if there's any + // possible time to wake up known earlier than that. + + // Note that in case of the "builtin group reader" (its own + // buffer), there's no need to do it here because it has also + // its own TSBPD thread. + + if (info.result == CRcvBuffer::InsertInfo::INSERTED) + { + // This may happen multiple times in the loop, so update only if earlier. + if (w_next_tsbpd == time_point() || w_next_tsbpd > info.first_time) + w_next_tsbpd = info.first_time; + w_new_inserted = true; + } + buffer_add_result = int(info.result); + if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -9797,8 +9859,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - w_new_inserted = true; - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) @@ -10107,6 +10167,7 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), + (next_tsbpd_avail), (was_sent_in_order), (srt_loss_seqs)); @@ -10181,6 +10242,42 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + // 1. This is set to true in case when TSBPD during the last check + // has seen no packet candidate to ever deliver, hence it needs + // an update on that. Note that this is also false if TSBPD thread + // isn't running. + // 2. If next_tsbpd_avail is set, it means that in the buffer there is + // a new packet that precedes the previously earliest available packet. + // This means that if TSBPD was sleeping up to the time of this earliest + // delivery (after drop), this time we have received a packet to be delivered + // earlier than that, so we need to notify TSBPD immediately so that it + // updates this itself, not sleep until the previously set time. + + // The meaning of m_bWakeOnRecv: + // - m_bWakeOnRecv is set by TSBPD thread and means that it wishes to be woken up + // on every received packet. Hence we signal always if a new packet was inserted. + // - even if TSBPD doesn't wish to be woken up on every reception (because it sleeps + // until the play time of the next deliverable packet), it will be woken up when + // next_tsbpd_avail is set because it means this time is earlier than the time until + // which TSBPD sleeps, so it must be woken up prematurely. It might be more performant + // to simply update the sleeping end time of TSBPD, but there's no way to do it, so + // we simply wake TSBPD up and count on that it will update its sleeping settings. + + // XXX Consider: as CUniqueSync locks m_RecvLock, it means that the next instruction + // gets run only when TSBPD falls asleep again. Might be a good idea to record the + // TSBPD end sleeping time - as an alternative to m_bWakeOnRecv - and after locking + // a mutex check this time again and compare it against next_tsbpd_avail; might be + // that if this difference is smaller than "dirac" (could be hard to reliably compare + // this time, unless it's set from this very value), there's no need to wake the TSBPD + // thread because it will wake up on time requirement at the right time anyway. + if (m_bTsbPd && ((m_bWakeOnRecv && new_inserted) || next_tsbpd_avail != time_point())) + { + HLOGC(qrlog.Debug, log << "processData: will SIGNAL TSBPD for socket. WakeOnRecv=" << m_bWakeOnRecv + << " new_inserted=" << new_inserted << " next_tsbpd_avail=" << FormatTime(next_tsbpd_avail)); + CUniqueSync tsbpd_cc(m_RecvLock, m_RcvTsbPdCond); + tsbpd_cc.notify_all(); + } + if (incoming.empty()) { // Treat as excessive. This is when a filter cumulates packets @@ -10197,6 +10294,10 @@ int srt::CUDT::processData(CUnit* in_unit) sendLossReport(srt_loss_seqs); } + // This should not be required with the new receiver buffer because + // signalling happens on every packet reception, if it has changed the + // earliest packet position. + /* if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10206,6 +10307,7 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } + */ } // Separately report loss records of those reported by a filter. @@ -10218,6 +10320,8 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); + // XXX unsure as to whether this should change anything in the TSBPD conditions. + // Might be that this trigger is not necessary. if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10357,67 +10461,6 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) setInitialRcvSeq(source->m_iRcvLastSkipAck); } -// XXX This function is currently unused. It should be fixed and put into use. -// See the blocked call in CUDT::processData(). -// XXX REVIEW LOCKS WHEN REACTIVATING! -srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) -{ -// [[using affinity(m_pRcvBuffer->workerThread())]]; - CUDT* self = (CUDT*)vself; - loss_seqs_t output; - - // XXX When an alternative packet arrival callback is installed - // in case of groups, move this part to the groupwise version. - - if (self->m_parent->m_GroupOf) - { - groups::SocketData* gi = self->m_parent->m_GroupMemberData; - if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely - { - HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); - gi->rcvstate = SRT_GST_RUNNING; - return output; - } - } - - const int initial_loss_ttl = (self->m_bPeerRexmitFlag) ? self->m_iReorderTolerance : 0; - - int seqdiff = CSeqNo::seqcmp(pkt.m_iSeqNo, self->m_iRcvCurrSeqNo); - - HLOGC(qrlog.Debug, log << "defaultPacketArrival: checking sequence " << pkt.m_iSeqNo - << " against latest " << self->m_iRcvCurrSeqNo << " (distance: " << seqdiff << ")"); - - // Loss detection. - if (seqdiff > 1) // packet is later than the very subsequent packet - { - const int32_t seqlo = CSeqNo::incseq(self->m_iRcvCurrSeqNo); - const int32_t seqhi = CSeqNo::decseq(pkt.m_iSeqNo); - - { - // If loss found, insert them to the receiver loss list - ScopedLock lg (self->m_RcvLossLock); - self->m_pRcvLossList->insert(seqlo, seqhi); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - self->m_FreshLoss.push_back(CRcvFreshLoss(seqlo, seqhi, initial_loss_ttl)); - HLOGF(qrlog.Debug, "defaultPacketArrival: added loss sequence %d-%d (%d) with tolerance %d", seqlo, seqhi, - 1+CSeqNo::seqcmp(seqhi, seqlo), initial_loss_ttl); - } - } - - if (!initial_loss_ttl) - { - // old code; run immediately when tolerance = 0 - // or this feature isn't used because of the peer - output.push_back(make_pair(seqlo, seqhi)); - } - } - - return output; -} #endif /// This function is called when a packet has arrived, which was behind the current @@ -11089,7 +11132,10 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). */ + enterCS(m_RcvLossLock); const int loss_len = m_pRcvLossList->getLossLength(); + leaveCS(m_RcvLossLock); + SRT_ASSERT(loss_len >= 0); int debug_decision = BECAUSE_NO_REASON; diff --git a/srtcore/core.h b/srtcore/core.h index 81979bb83..5bb66d436 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -661,6 +661,7 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM); void handshakeDone() @@ -925,7 +926,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1061,13 +1062,14 @@ class CUDT /// /// @param incoming [in] The packet coming from the network medium /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_next_tsbpd [out] Get the TSBPD time of the earliest playable packet after insertion /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. /// /// @return 0 The call was successful (regardless if the packet was accepted or not). /// @return -1 The call has failed: no space left in the buffer. /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, sync::steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // This function is to return the packet's play time (time when // it is submitted to the reading application) of the given packet. diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 4b0d9c833..7d70d6d92 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -72,7 +72,10 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } - return m_rcv_buffer->insert(unit); + auto info = m_rcv_buffer->insert(unit); + // XXX extra checks? + + return int(info.result); } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed From 36c1f674f39a1c4b116c0496ed873936a8a78ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 8 Nov 2022 17:24:59 +0100 Subject: [PATCH 025/174] Updated usage of shortcuts and new names --- srtcore/buffer_rcv.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ede2033cb..3c0ce3e5c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,15 +75,15 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const int iLastPos = (iStartPos + iMaxPosOff) % iSize; const bool isOverrun = iLastPos < iStartPos; if (isOverrun) @@ -499,7 +499,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) continue; // TODO: Break the loop if a massege has been found. No need to search further. - const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); + const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { ++iDropCnt; @@ -551,7 +551,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { // Don't drop messages, if all its packets are already in the buffer. // TODO: Don't drop a several-packet message if all packets are in the buffer. - if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + if (m_entries[i].pUnit && packetAt(i).getMsgBoundary() == PB_SOLO) continue; dropUnitInPos(i); @@ -636,7 +636,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairm_Packet; + const CPacket& packet = packetAt(i); const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); @@ -828,7 +828,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) return -1; } - const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + const srt::CPacket& pkt = packetAt(p); if (bTsbPdEnabled) { @@ -935,8 +935,8 @@ int CRcvBuffer::getTimespan_ms() const return 0; const steady_clock::time_point startstamp = - getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); - const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + getPktTsbPdTime(packetAt(startpos).getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(packetAt(lastpos).getMsgTimeStamp()); if (endstamp < startstamp) return 0; @@ -1006,7 +1006,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no { if (hasInorderPackets) { - const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iStartPos); const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } @@ -1014,7 +1014,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no if (m_iFirstRandomMsgPos >= 0) { SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iFirstRandomMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1055,9 +1055,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) return false; if (m_tsbpd.isEnabled()) { - updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + updateTsbPdTimeBase(packetAt(pos).getMsgTimeStamp()); } - else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { --m_numRandomPackets; if (pos == m_iFirstRandomMsgPos) @@ -1128,7 +1128,7 @@ int CRcvBuffer::findLastMessagePkt() { SRT_ASSERT(m_entries[i].pUnit); - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (packetAt(i).getMsgBoundary() & PB_LAST) { return i; } @@ -1193,7 +1193,7 @@ bool CRcvBuffer::checkFirstReadableRandom() if (!m_entries[pos].pUnit) return false; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) return false; @@ -1236,7 +1236,7 @@ void CRcvBuffer::updateFirstReadableRandom() continue; } - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) // Skip in order packet { @@ -1287,7 +1287,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) break; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1376,7 +1376,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1392,7 +1392,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (m_entries[iLastPos].pUnit) { ss << ", timespan "; - const uint32_t usPktTimestamp = m_entries[iLastPos].pUnit->m_Packet.getMsgTimeStamp(); + const uint32_t usPktTimestamp = packetAt(iLastPos).getMsgTimeStamp(); ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - nextValidPkt.tsbpd_time); ss << " ms"; } From 8335cbec1a7477a387be35f470e068779ccef41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 10 Nov 2022 14:58:42 +0100 Subject: [PATCH 026/174] Fixed some logs formatting --- srtcore/core.cpp | 34 +++++++++++++++++++++++----------- srtcore/packet.cpp | 22 ++++++++++++++++++---- srtcore/queue.cpp | 4 ++-- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b480acbd3..950cb0b72 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5274,11 +5274,20 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; - HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" - << info.seqno << " T=" << FormatTime(tsNextDelivery) - << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) - << " ready=" << is_time_to_deliver - << " ondrop=" << info.seq_gap); +#if ENABLE_HEAVY_LOGGING + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: NO PACKETS"); + } + else + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + } +#endif if (!self->m_bTLPktDrop) { @@ -5316,7 +5325,7 @@ void * srt::CUDT::tsbpd(void* param) { HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + << FormatDuration(steady_clock::now() - info.tsbpd_time) << ")"); /* * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available @@ -5375,6 +5384,8 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } + SRT_ATR_UNUSED bool wakeup_on_signal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5385,9 +5396,9 @@ void * srt::CUDT::tsbpd(void* param) self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5410,7 +5421,8 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); @@ -7488,8 +7500,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) m_dCongestionWindow = m_CongCtl->cgWindowSize(); #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, - log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us (" - << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << m_dCongestionWindow); #endif } diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index cbe4dd90d..e7ddb9db1 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -263,7 +263,7 @@ void CPacket::setLength(size_t len, size_t cap) #if ENABLE_HEAVY_LOGGING // Debug only -static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, const size_t size) { // This may be changed over time, so use special interpretation // only for certain types, and still display all data, no matter @@ -288,9 +288,15 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } bool interp_as_seq = (pkttype == UMSG_LOSSREPORT || pkttype == UMSG_DROPREQ); + bool display_dec = (pkttype == UMSG_ACK || pkttype == UMSG_ACKACK || pkttype == UMSG_DROPREQ); out << " [ "; - for (size_t i = 0; i < size; ++i) + + // Will be effective only for hex/oct. + out << std::showbase; + + const size_t size32 = size/4; + for (size_t i = 0; i < size32; ++i) { int32_t val = ((int32_t*)rparam)[i]; if (interp_as_seq) @@ -302,8 +308,16 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } else { - out << std::showpos << std::hex << val << "/" << std::dec << val; + if (!display_dec) + { + out << std::hex; + out << val << "/"; + out << std::dec; + } + out << val; + } + out << " "; } out << "]"; @@ -315,7 +329,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); - HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << FormatNumbers(pkttype, lparam, rparam, size)); + HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << " " << FormatNumbers(pkttype, lparam, rparam, size)); // Set additional information and control information field switch (pkttype) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8d6727268..1627a0c6b 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1042,8 +1042,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << " " << FormatDuration(tsNow - tsLastReq) + << " passed since last connection request."); continue; } From 2424c99c427b5036413bfd1e50092aec7222f634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Dec 2022 17:39:04 +0100 Subject: [PATCH 027/174] Fixing implicit integer and wrong use of SRT_ERROR and SRT_INVALID_SOCKET --- CMakeLists.txt | 4 + apps/socketoptions.hpp | 2 +- common/devel_util.h | 74 +++++++ examples/recvlive.cpp | 10 - examples/recvmsg.cpp | 215 ++++++++++++++++++ examples/sendmsg.cpp | 210 ++++++++++++++++++ examples/test-c-client.c | 2 +- examples/test-c-server.c | 2 +- srtcore/api.cpp | 408 ++++++++++++++++++----------------- srtcore/api.h | 60 +++--- srtcore/buffer_rcv.cpp | 4 + srtcore/buffer_snd.cpp | 52 +++-- srtcore/buffer_snd.h | 25 ++- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 307 ++++++++++++++------------ srtcore/core.h | 61 +++--- srtcore/crypto.cpp | 2 +- srtcore/epoll.cpp | 24 +-- srtcore/epoll.h | 23 +- srtcore/fec.cpp | 2 +- srtcore/group.cpp | 6 +- srtcore/group.h | 2 +- srtcore/handshake.cpp | 4 +- srtcore/handshake.h | 2 +- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 62 +++--- srtcore/queue.h | 32 +-- srtcore/srt.h | 105 ++++----- srtcore/srt_c_api.cpp | 90 ++++---- srtcore/tsbpd_time.cpp | 2 +- srtcore/udt.h | 2 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +- test/test_crypto.cpp | 6 +- test/test_epoll.cpp | 24 +-- test/test_fec_rebuilding.cpp | 6 +- testing/testmedia.cpp | 102 ++++----- testing/testmedia.hpp | 10 +- 41 files changed, 1280 insertions(+), 713 deletions(-) create mode 100644 common/devel_util.h create mode 100644 examples/recvmsg.cpp create mode 100644 examples/sendmsg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4de83e89e..01c4eef17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1325,6 +1325,10 @@ if (ENABLE_EXAMPLES) srt_add_example(recvfile.cpp) + srt_add_example(sendmsg.cpp) + + srt_add_example(recvmsg.cpp) + srt_add_example(test-c-client.c) srt_add_example(example-client-nonblock.c) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 7ca0451a2..9b9f7e73b 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -67,7 +67,7 @@ struct SocketOption template<> inline int SocketOption::setso(int socket, int /*ignored*/, int sym, const void* data, size_t size) { - return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_setsockopt(SRTSOCKET(socket), 0, SRT_SOCKOPT(sym), data, (int) size); } #if ENABLE_BONDING diff --git a/common/devel_util.h b/common/devel_util.h new file mode 100644 index 000000000..1944e2613 --- /dev/null +++ b/common/devel_util.h @@ -0,0 +1,74 @@ + +template +struct IntWrapper +{ + INT v; + + IntWrapper() {} + explicit IntWrapper(INT val): v(val) {} + + bool operator==(const IntWrapper& x) const + { + return v == x.v; + } + + bool operator!=(const IntWrapper& x) const + { + return !(*this == x); + } + + explicit operator INT() const + { + return v; + } + + template + friend T& operator<<(T& out, const IntWrapper& x) + { + out << x.v; + return out; + } + + bool operator<(const IntWrapper& w) const + { + return v < w.v; + } +}; + +template +struct IntWrapperLoose: IntWrapper +{ + typedef IntWrapper base_t; + explicit IntWrapperLoose(INT val): base_t(val) {} + + bool operator==(const IntWrapper& x) const + { + return this->v == x.v; + } + + friend bool operator==(const IntWrapper& x, const IntWrapperLoose& y) + { + return x.v == y.v; + } + + bool operator==(INT val) const + { + return this->v == val; + } + + friend bool operator==(INT val, const IntWrapperLoose& x) + { + return val == x.v; + } + + operator INT() const + { + return this->v; + } +}; + + +//typedef IntWrapper SRTSOCKET; +//typedef IntWrapper SRTSTATUS; +//typedef IntWrapperLoose SRTSTATUS_LOOSE; + diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index b2a026d03..81c2b1ef7 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -53,16 +53,6 @@ int main(int argc, char* argv[]) return 0; } - // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, - // although there's also lots of other options to be set, for which there's a convenience option, - // SRTO_TRANSTYPE. - // SRT_TRANSTYPE tt = SRTT_LIVE; - // if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_TRANSTYPE, &tt, sizeof tt)) - // { - // cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; - // return 0; - // } - bool no = false; if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_RCVSYN, &no, sizeof no)) { diff --git a/examples/recvmsg.cpp b/examples/recvmsg.cpp new file mode 100644 index 000000000..008eb3928 --- /dev/null +++ b/examples/recvmsg.cpp @@ -0,0 +1,215 @@ +#ifndef WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +string CreateFilename(string fmt, int ord) +{ + ostringstream os; + + size_t pos = fmt.find('%'); + if (pos == string::npos) + os << fmt << ord << ".out"; + else + { + os << fmt.substr(0, pos) << ord << fmt.substr(pos+1); + } + return os.str(); +} + +int main(int argc, char* argv[]) +{ + string service("9000"); + if (argc > 1) + service = argv[1]; + + if (service == "--help") + { + cout << "usage: recvmsg [server_port] [filepattern]" << endl; + return 0; + } + + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) + { + cout << "illegal port number or port is busy.\n" << endl; + return 1; + } + + string outfileform; + if (argc > 2) + { + outfileform = argv[2]; + } + + // use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + SRTSOCKET sfd = srt_create_socket(); + if (SRT_INVALID_SOCK == sfd) + { + cout << "srt_socket: " << srt_getlasterror_str() << endl; + return 1; + } + + int file_mode = SRTT_FILE; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_TRANSTYPE, &file_mode, sizeof file_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + bool message_mode = true; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + if (SRT_ERROR == srt_bind(sfd, res->ai_addr, res->ai_addrlen)) + { + cout << "srt_bind: " << srt_getlasterror_str() << endl; + return 0; + } + + freeaddrinfo(res); + + cout << "server is ready at port: " << service << endl; + + if (SRT_ERROR == srt_listen(sfd, 10)) + { + cout << "srt_listen: " << srt_getlasterror_str() << endl; + return 1; + } + + char data[4096]; + + srt::sockaddr_any remote; + + int afd = srt_accept(sfd, remote.get(), &remote.len); + + if (afd == SRT_INVALID_SOCK) + { + cout << "srt_accept: " << srt_getlasterror_str() << endl; + return 1; + } + + cout << "Connection from " << remote.str() << " established\n"; + + bool save_to_files = true; + + if (outfileform != "") + save_to_files = true; + + int ordinal = 1; + + // the event loop + while (true) + { + SRT_SOCKSTATUS status = srt_getsockstate(afd); + if ((status == SRTS_BROKEN) || + (status == SRTS_NONEXIST) || + (status == SRTS_CLOSED)) + { + cout << "source disconnected. status=" << status << endl; + srt_close(afd); + break; + } + + int ret = srt_recvmsg(afd, data, sizeof(data)); + if (ret == SRT_ERROR) + { + cout << "srt_recvmsg: " << srt_getlasterror_str() << endl; + break; + } + if (ret == 0) + { + cout << "EOT\n"; + break; + } + + if (ret < 5) + { + cout << "WRONG MESSAGE SYNTAX\n"; + break; + } + + if (save_to_files) + { + string fname = CreateFilename(outfileform, ordinal++); + ofstream ofile(fname); + + if (!ofile.good()) + { + cout << "ERROR: can't create file: " << fname << " - skipping message\n"; + continue; + } + + ofile.write(data, ret); + ofile.close(); + cout << "Written " << ret << " bytes of message to " << fname << endl; + } + else + { + union + { + char chars[4]; + int32_t intval; + } first4; + + copy(data, data + 4, first4.chars); + + cout << "[" << ret << "B " << ntohl(first4.intval) << "] "; + for (int i = 4; i < ret; ++i) + cout << ShowChar(data[i]); + cout << endl; + } + } + + srt_close(afd); + srt_close(sfd); + + // use this function to release the UDT library + srt_cleanup(); + + return 0; +} + +// Local Variables: +// c-file-style: "ellemtel" +// c-basic-offset: 3 +// compile-command: "g++ -Wall -O2 -std=c++11 -I.. -I../srtcore -o recvlive recvlive.cpp -L.. -lsrt -lpthread -L/usr/local/opt/openssl/lib -lssl -lcrypto" +// End: diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp new file mode 100644 index 000000000..911de48c9 --- /dev/null +++ b/examples/sendmsg.cpp @@ -0,0 +1,210 @@ +#ifndef _WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +int main(int argc, char* argv[]) +{ + if ((argc != 4) || (0 == atoi(argv[2]))) + { + cout << "usage: sendmsg server_ip server_port source_filename" << endl; + return -1; + } + + // Use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + struct addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + SRTSOCKET fhandle = srt_create_socket(); + // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, + // although there's also lots of other options to be set, for which there's a convenience option, + // SRTO_TRANSTYPE. + SRT_TRANSTYPE tt = SRTT_FILE; + srt_setsockopt(fhandle, 0, SRTO_TRANSTYPE, &tt, sizeof tt); + + bool message_mode = true; + srt_setsockopt(fhandle, 0, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode); + + if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) + { + cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; + return -1; + } + + // Connect to the server, implicit bind. + if (SRT_ERROR == srt_connect(fhandle, peer->ai_addr, peer->ai_addrlen)) + { + int rej = srt_getrejectreason(fhandle); + cout << "connect: " << srt_getlasterror_str() << ":" << srt_rejectreason_str(rej) << endl; + return -1; + } + + freeaddrinfo(peer); + + string source_fname = argv[3]; + + bool use_filelist = false; + if (source_fname[0] == '+') + { + use_filelist = true; + source_fname = source_fname.substr(1); + } + + istream* pin; + ifstream fin; + if (source_fname == "-") + { + pin = &cin; + } + else + { + fin.open(source_fname.c_str()); + pin = &fin; + } + + // The syntax is: + // + // 1. If the first character is +, it's followed by TTL in milliseconds. + // 2. Otherwise the first number is the ID, followed by a space, to be filled in first 4 bytes. + // 3. Rest of the characters, up to the end of line, should be put into a solid block and sent at once. + + int status = SRT_STATUS_OK; + + int ordinal = 1; + int lpos = 0; + + for (;;) + { + string line; + int32_t id = 0; + int ttl = -1; + + getline(*pin, (line)); + if (pin->eof()) + break; + + // Interpret + if (line.size() < 2) + continue; + + if (use_filelist) + { + // The line should contain [+TTL] FILENAME + char fname[1024] = ""; + if (line[0] == '+') + { + int nparsed = sscanf(line.c_str(), "+%d %n%1000s", &ttl, &lpos, fname); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + line = fname; + } + + ifstream ifile (line); + + id = ordinal; + ++ordinal; + + if (!ifile.good()) + { + cout << "ERROR: file '" << line << "' cannot be read, skipping\n"; + continue; + } + + line = string(istreambuf_iterator(ifile), istreambuf_iterator()); + } + else + { + int lpos = 0; + + int nparsed = 0; + if (line[0] == '+') + { + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + else + { + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + if (nparsed != 1) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + } + + union + { + char chars[4]; + int32_t intval; + } first4; + first4.intval = htonl(id); + + vector input; + copy(first4.chars, first4.chars+4, back_inserter(input)); + copy(line.begin() + lpos, line.end(), back_inserter(input)); + + // CHECK CODE + // cout << "WILL SEND: TTL=" << ttl << " "; + // transform(input.begin(), input.end(), + // ostream_iterator(cout), ShowChar); + // cout << endl; + + int nsnd = srt_sendmsg(fhandle, &input[0], input.size(), ttl, false); + if (nsnd == SRT_ERROR) + { + cout << "SRT ERROR: " << srt_getlasterror_str() << endl; + status = SRT_ERROR; + break; + } + } + + srt_close(fhandle); + + // Signal to the SRT library to clean up all allocated sockets and resources. + srt_cleanup(); + + return status; +} diff --git a/examples/test-c-client.c b/examples/test-c-client.c index a4d99745c..71f6d30e4 100644 --- a/examples/test-c-client.c +++ b/examples/test-c-client.c @@ -59,7 +59,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/examples/test-c-server.c b/examples/test-c-server.c index f81f297eb..f7faf6fba 100644 --- a/examples/test-c-server.c +++ b/examples/test-c-server.c @@ -54,7 +54,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 1cd67cea5..78ba3a346 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -229,7 +229,7 @@ srt::CUDTUnited::~CUDTUnited() string srt::CUDTUnited::CONID(SRTSOCKET sock) { - if (sock == 0) + if (int32_t(sock) <= 0) // both SRT_INVALID_SOCK and SRT_SOCKID_CONNREQ and illegal negative domain return ""; std::ostringstream os; @@ -237,12 +237,12 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -int srt::CUDTUnited::startup() +SRTSTATUS srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return 1; + return SRTSTATUS(1); // Global initialization code #ifdef _WIN32 @@ -259,21 +259,21 @@ int srt::CUDTUnited::startup() PacketFilter::globalInit(); if (m_bGCStatus) - return 1; + return SRTSTATUS(1); m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) - return -1; + return SRT_ERROR; m_bGCStatus = true; HLOGC(inlog.Debug, log << "SRT Clock Type: " << SRT_SYNC_CLOCK_STR); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::cleanup() +SRTSTATUS srt::CUDTUnited::cleanup() { // IMPORTANT!!! // In this function there must be NO LOGGING AT ALL. This function may @@ -286,10 +286,10 @@ int srt::CUDTUnited::cleanup() ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) - return 0; + return SRT_STATUS_OK; if (!m_bGCStatus) - return 0; + return SRT_STATUS_OK; { UniqueLock gclock(m_GCStopLock); @@ -310,7 +310,7 @@ int srt::CUDTUnited::cleanup() WSACleanup(); #endif - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) @@ -360,10 +360,10 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) const bool exists = #if ENABLE_BONDING for_group - ? m_Groups.count(sockval | SRTGROUP_MASK) + ? m_Groups.count(SRTSOCKET(sockval | SRTGROUP_MASK)) : #endif - m_Sockets.count(sockval); + m_Sockets.count(SRTSOCKET(sockval)); leaveCS(m_GlobControlLock); if (exists) @@ -412,7 +412,7 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) LOGC(smlog.Debug, log << "generateSocketID: " << (for_group ? "(group)" : "") << ": @" << sockval); - return sockval; + return SRTSOCKET(sockval); } SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) @@ -442,7 +442,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) throw; } ns->m_Status = SRTS_INIT; - ns->m_ListenSocket = 0; + ns->m_ListenSocket = SRT_SOCKID_CONNREQ; // A value used for socket if it wasn't listener-spawned ns->core().m_SocketID = ns->m_SocketID; ns->core().m_pCache = m_pCache; @@ -470,7 +470,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } -int srt::CUDTUnited::newConnection(const SRTSOCKET listen, +int srt::CUDTUnited::newConnection(const SRTSOCKET listener, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs, @@ -484,15 +484,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // Can't manage this error through an exception because this is // running in the listener loop. - CUDTSocket* ls = locateSocket(listen); + CUDTSocket* ls = locateSocket(listener); if (!ls) { - LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listen << " which DOES NOT EXIST."); + LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listener << " which DOES NOT EXIST."); return -1; } HLOGC(cnlog.Debug, - log << "newConnection: creating new socket after listener @" << listen + log << "newConnection: creating new socket after listener @" << listener << " contacted with backlog=" << ls->m_uiBackLog); // if this connection has already been processed @@ -572,13 +572,13 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return -1; } - ns->m_ListenSocket = listen; + ns->m_ListenSocket = listener; 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->core().m_SocketID + log << "newConnection: DATA: lsnid=" << listener << " id=" << ns->core().m_SocketID << " peerid=" << ns->core().m_PeerID << " ISN=" << ns->m_iISN); int error = 0; @@ -769,7 +769,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, 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->core().m_sPollID, SRT_EPOLL_ACCEPT, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); CGlobEvent::triggerEvent(); @@ -790,7 +790,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // 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->core().m_sPollID, SRT_EPOLL_UPDATE, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); CGlobEvent::triggerEvent(); } @@ -838,12 +838,12 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // static forwarder -int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return uglobal().installAcceptHook(lsn, hook, opaq); } -int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { try { @@ -856,24 +856,24 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } -int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return uglobal().installConnectHook(lsn, hook, opaq); } -int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) { try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->installConnectHook(hook, opaq); - return 0; + return SRT_STATUS_OK; } #endif CUDTSocket* s = locateSocket(u, ERH_THROW); @@ -885,7 +885,7 @@ int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) @@ -905,7 +905,7 @@ SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } -int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -920,10 +920,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) { ScopedLock cg(s->m_ControlLock); @@ -949,10 +949,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr(s->m_SelfAddr); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) { if (backlog <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -975,7 +975,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // do nothing if the socket is already listening if (s->m_Status == SRTS_LISTENING) - return 0; + return SRT_STATUS_OK; // a socket can listen only if is in OPENED status if (s->m_Status != SRTS_OPENED) @@ -995,7 +995,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // if thrown, remains in OPENED state if so. s->m_Status = SRTS_LISTENING; - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut) @@ -1037,7 +1037,7 @@ SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, i // Theoretically we can have a situation that more than one // listener is ready for accept. In this case simply get // only the first found. - int lsn = st.begin()->first; + SRTSOCKET lsn = st.begin()->first; sockaddr_storage dummy; int outlen = sizeof dummy; return accept(lsn, ((sockaddr*)&dummy), (&outlen)); @@ -1077,7 +1077,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); } - SRTSOCKET u = CUDT::INVALID_SOCK; + SRTSOCKET u = SRT_INVALID_SOCK; bool accepted = false; // !!only one connection can be set up each time!! @@ -1110,7 +1110,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, false); } - if (u == CUDT::INVALID_SOCK) + if (u == SRT_INVALID_SOCK) { // non-blocking receiving, no connection available if (!ls->core().m_config.bSynRecving) @@ -1178,7 +1178,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int return u; } -int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) +SRTSOCKET srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified if (!srcname || !tarname || namelen < int(sizeof(sockaddr_in))) @@ -1200,7 +1200,7 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); // Note: forced_isn is ignored when connecting a group. @@ -1221,10 +1221,11 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // For a single socket, just do bind, then connect bind(s, source_addr); - return connectIn(s, target_addr, SRT_SEQNO_NONE); + connectIn(s, target_addr, SRT_SEQNO_NONE); + return SRT_SOCKID_CONNREQ; } -int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { if (!name || namelen < int(sizeof(sockaddr_in))) { @@ -1240,7 +1241,7 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); @@ -1257,31 +1258,29 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - return connectIn(s, target_addr, forced_isn); + connectIn(s, target_addr, forced_isn); + return SRT_SOCKID_CONNREQ; } #if ENABLE_BONDING -int srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) +SRTSOCKET srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) { - int gstat = groupConnect(pg, gd, 1); - if (gstat == -1) + SRTSOCKET gstat = groupConnect(pg, gd, 1); + if (gstat == SRT_INVALID_SOCK) { // We have only one element here, so refer to it. // Sanity check if (gd->errorcode == SRT_SUCCESS) gd->errorcode = SRT_EINVPARAM; - CodeMajor mj = CodeMajor(gd->errorcode / 1000); - CodeMinor mn = CodeMinor(gd->errorcode % 1000); - - return CUDT::APIError(mj, mn); + return CUDT::APIError(gd->errorcode), SRT_INVALID_SOCK; } return gstat; } // [[using assert(pg->m_iBusy > 0)]] -int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) +SRTSOCKET srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) { CUDTGroup& g = *pg; SRT_ASSERT(g.m_iBusy > 0); @@ -1323,7 +1322,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // This also should happen if ERR flag was set, as IN and OUT could be set, too. m_EPoll.update_events(g.id(), g.m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } - SRTSOCKET retval = -1; + SRTSOCKET retval = SRT_INVALID_SOCK; int eid = -1; int connect_modes = SRT_EPOLL_CONNECT | SRT_EPOLL_ERR; @@ -1470,7 +1469,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } else { - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; delete ns; m_Sockets.erase(sid); @@ -1532,7 +1531,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // to avoid locking more than one mutex at a time. erc_rloc = e.getErrorCode(); targets[tii].errorcode = e.getErrorCode(); - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); @@ -1545,7 +1544,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i { LOGC(aclog.Fatal, log << "groupConnect: IPE: UNKNOWN EXCEPTION from connectIn"); targets[tii].errorcode = SRT_ESYSOBJ; - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); m_Sockets.erase(ns->m_SocketID); @@ -1594,7 +1593,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // set BROKEN or PENDING. f->sndstate = SRT_GST_PENDING; f->rcvstate = SRT_GST_PENDING; - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -1642,7 +1641,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } } - if (retval == -1) + if (retval == SRT_INVALID_SOCK) { HLOGC(aclog.Debug, log << "groupConnect: none succeeded as background-spawn, exit with error"); block_new_opened = false; // Avoid executing further while loop @@ -1655,13 +1654,13 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i if (spawned.empty()) { // All were removed due to errors. - retval = -1; + retval = SRT_INVALID_SOCK; break; } HLOGC(aclog.Debug, log << "groupConnect: first connection, applying EPOLL WAITING."); int len = (int)spawned.size(); vector ready(spawned.size()); - const int estat = srt_epoll_wait(eid, + const int estat = srt_epoll_wait(eid, NULL, NULL, // IN/ACCEPT &ready[0], @@ -1673,7 +1672,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i NULL); // Sanity check. Shouldn't happen if subs are in sync with spawned. - if (estat == -1) + if (estat == int(SRT_ERROR)) { #if ENABLE_LOGGING CUDTException& x = CUDT::getlasterror(); @@ -1685,7 +1684,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } #endif HLOGC(aclog.Debug, log << "groupConnect: srt_epoll_wait failed - breaking the wait loop"); - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -1807,14 +1806,14 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // standing. Each one could, however, break by a different reason, // for example, one by timeout, another by wrong passphrase. Check // the `errorcode` field to determine the reaon for particular link. - if (retval == -1) + if (retval == SRT_INVALID_SOCK) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); return retval; } #endif -int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) +void 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: @@ -1876,19 +1875,17 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i s->m_Status = SRTS_OPENED; throw; } - - return 0; } -int srt::CUDTUnited::close(const SRTSOCKET u) +SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->close(); deleteGroup(k.group); - return 0; + return SRT_STATUS_OK; } #endif CUDTSocket* s = locateSocket(u); @@ -1946,7 +1943,7 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int srt::CUDTUnited::close(CUDTSocket* s) +SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); @@ -1959,7 +1956,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if (s->m_Status == SRTS_LISTENING) { if (s->core().m_bBroken) - return 0; + return SRT_STATUS_OK; s->m_tsClosureTimeStamp = steady_clock::now(); s->core().m_bBroken = true; @@ -2007,7 +2004,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) { HLOGC(smlog.Debug, log << "@" << u << "U::close: NOT AN ACTIVE SOCKET, returning."); - return 0; + return SRT_STATUS_OK; } s = i->second; s->setClosed(); @@ -2104,7 +2101,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } */ - return 0; + return SRT_STATUS_OK; } void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) @@ -2330,59 +2327,55 @@ int srt::CUDTUnited::epoll_create() return m_EPoll.create(); } -int srt::CUDTUnited::epoll_clear_usocks(int eid) +void srt::CUDTUnited::epoll_clear_usocks(int eid) { return m_EPoll.clear_usocks(eid); } -int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +void srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { - int ret = -1; #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); - ret = m_EPoll.update_usock(eid, u, events); + m_EPoll.update_usock(eid, u, events); k.group->addEPoll(eid); - return 0; + return; } #endif CUDTSocket* s = locateSocket(u); if (s) { - ret = epoll_add_usock_INTERNAL(eid, s, events); + epoll_add_usock_INTERNAL(eid, s, events); } else { throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); } - - return ret; } // NOTE: WILL LOCK (serially): // - CEPoll::m_EPollLock // - CUDT::m_RecvLock -int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) +void 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); + m_EPoll.update_usock(eid, s->m_SocketID, events); s->core().addEPoll(eid); - return ret; } -int srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } -int srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.update_ssock(eid, s, events); } template -int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) +void 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. @@ -2398,31 +2391,29 @@ int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) HLOGC(ealog.Debug, log << "epoll_remove_usock: CLEARING subscription on E" << eid << " of @" << ent->id()); int no_events = 0; - int ret = m_EPoll.update_usock(eid, ent->id(), &no_events); - - return ret; + m_EPoll.update_usock(eid, ent->id(), &no_events); } // Needed internal access! -int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) +void srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { return epoll_remove_entity(eid, &s->core()); } #if ENABLE_BONDING -int srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) +void srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) { return epoll_remove_entity(eid, g); } #endif -int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +void srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { CUDTSocket* s = 0; #if ENABLE_BONDING CUDTGroup* g = 0; - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); g = k.group; @@ -2442,7 +2433,7 @@ int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) return m_EPoll.update_usock(eid, u, &no_events); } -int srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +void srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } @@ -2457,7 +2448,7 @@ int32_t srt::CUDTUnited::epoll_set(int eid, int32_t flags) return m_EPoll.setflags(eid, flags); } -int srt::CUDTUnited::epoll_release(const int eid) +void srt::CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } @@ -2626,18 +2617,21 @@ void srt::CUDTUnited::checkBrokenSockets() 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()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } + // 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); + 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(); j != m_ClosedSockets.end(); ++j) @@ -2810,7 +2804,7 @@ void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int 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; + w_m.m_iID = int32_t(s->m_SocketID); } uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) @@ -3133,18 +3127,21 @@ void* srt::CUDTUnited::garbageCollect(void* p) #endif self->m_ClosedSockets[i->first] = s; - // remove from listener's queue - sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); - if (ls == self->m_Sockets.end()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = self->m_ClosedSockets.find(s->m_ListenSocket); - if (ls == self->m_ClosedSockets.end()) - continue; - } + // remove from listener's queue + sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); + if (ls == self->m_Sockets.end()) + { + ls = self->m_ClosedSockets.find(s->m_ListenSocket); + if (ls == self->m_ClosedSockets.end()) + continue; + } - enterCS(ls->second->m_AcceptLock); - ls->second->m_QueuedSockets.erase(s->m_SocketID); - leaveCS(ls->second->m_AcceptLock); + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); + } } self->m_Sockets.clear(); @@ -3176,12 +3173,12 @@ void* srt::CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -int srt::CUDT::startup() +SRTSTATUS srt::CUDT::startup() { return uglobal().startup(); } -int srt::CUDT::cleanup() +SRTSTATUS srt::CUDT::cleanup() { return uglobal().cleanup(); } @@ -3197,19 +3194,19 @@ SRTSOCKET srt::CUDT::socket() } catch (const CUDTException& e) { - SetThreadLocalError(e); - return INVALID_SOCK; + APIError a(e); + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { - SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + APIError a(MJ_SYSTEMRES, MN_MEMORY, 0); + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "socket: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + APIError a(MJ_UNKNOWN, MN_NONE, 0); + return SRT_INVALID_SOCK; } } @@ -3223,6 +3220,13 @@ srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) SetThreadLocalError(CUDTException(mj, mn, syserr)); } +srt::CUDT::APIError::APIError(int errorcode) +{ + CodeMajor mj = CodeMajor(errorcode / 1000); + CodeMinor mn = CodeMinor(errorcode % 1000); + SetThreadLocalError(CUDTException(mj, mn, 0)); +} + #if ENABLE_BONDING // This is an internal function; 'type' should be pre-checked if it has a correct value. // This doesn't have argument of GroupType due to header file conflicts. @@ -3254,14 +3258,14 @@ SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (...) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } - return SRT_INVALID_SOCK; + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } // [[using locked(m_ControlLock)]] @@ -3303,14 +3307,14 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) ScopedLock glock(uglobal().m_GlobControlLock); CUDTSocket* s = uglobal().locateSocket_LOCKED(socket); if (!s || !s->m_GroupOf) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; return s->m_GroupOf->id(); } int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { - if ((groupid & SRTGROUP_MASK) == 0 || !psize) + if (!isgroup(groupid) || !psize) { return APIError(MJ_NOTSUP, MN_INVAL, 0); } @@ -3326,7 +3330,7 @@ int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* } #endif -int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) { try { @@ -3360,7 +3364,7 @@ int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) } } -int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { @@ -3385,7 +3389,7 @@ int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) } } -int srt::CUDT::listen(SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDT::listen(SRTSOCKET u, int backlog) { try { @@ -3415,18 +3419,18 @@ SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept_bond: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } @@ -3439,22 +3443,22 @@ SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) { try { @@ -3462,29 +3466,29 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #if ENABLE_BONDING -int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) +SRTSOCKET srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) { if (arraysize <= 0) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; - if ((grp & SRTGROUP_MASK) == 0) + if (!isgroup(grp)) { // connectLinks accepts only GROUP id, not socket id. - return APIError(MJ_NOTSUP, MN_SIDINVAL, 0); + return APIError(MJ_NOTSUP, MN_SIDINVAL, 0), SRT_INVALID_SOCK; } try @@ -3494,21 +3498,21 @@ int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int ar } catch (CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #endif -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { try { @@ -3516,20 +3520,20 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t f } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } -int srt::CUDT::close(SRTSOCKET u) +SRTSTATUS srt::CUDT::close(SRTSOCKET u) { try { @@ -3546,12 +3550,12 @@ int srt::CUDT::close(SRTSOCKET u) } } -int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getpeername(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3564,12 +3568,12 @@ int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getsockname(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3582,7 +3586,7 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) +SRTSTATUS srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) { @@ -3592,17 +3596,17 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3615,7 +3619,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval } } -int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +SRTSTATUS srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { if (!optval) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3623,17 +3627,17 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3668,7 +3672,7 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); @@ -3712,7 +3716,7 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); @@ -3844,11 +3848,12 @@ int srt::CUDT::epoll_create() } } -int srt::CUDT::epoll_clear_usocks(int eid) +SRTSTATUS srt::CUDT::epoll_clear_usocks(int eid) { try { - return uglobal().epoll_clear_usocks(eid); + uglobal().epoll_clear_usocks(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3862,11 +3867,12 @@ int srt::CUDT::epoll_clear_usocks(int eid) } } -int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3879,11 +3885,12 @@ int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* even } } -int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_add_ssock(eid, s, events); + uglobal().epoll_add_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3896,11 +3903,12 @@ int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* even } } -int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3914,11 +3922,12 @@ int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* e } } -int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_update_ssock(eid, s, events); + uglobal().epoll_update_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3932,11 +3941,12 @@ int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* e } } -int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +SRTSTATUS srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { - return uglobal().epoll_remove_usock(eid, u); + uglobal().epoll_remove_usock(eid, u); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3950,11 +3960,12 @@ int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) } } -int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +SRTSTATUS srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { - return uglobal().epoll_remove_ssock(eid, s); + uglobal().epoll_remove_ssock(eid, s); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3998,12 +4009,12 @@ int srt::CUDT::epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), int(SRT_ERROR); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_uwait: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), int(SRT_ERROR); } } @@ -4024,11 +4035,12 @@ int32_t srt::CUDT::epoll_set(const int eid, int32_t flags) } } -int srt::CUDT::epoll_release(const int eid) +SRTSTATUS srt::CUDT::epoll_release(const int eid) { try { - return uglobal().epoll_release(eid); + uglobal().epoll_release(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4046,10 +4058,10 @@ srt::CUDTException& srt::CUDT::getlasterror() return GetThreadLocalError(); } -int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +SRTSTATUS srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) return groupsockbstats(u, perf, clear); #endif @@ -4057,7 +4069,7 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta { CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.bstats(perf, clear, instantaneous); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4071,24 +4083,24 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta } #if ENABLE_BONDING -int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) +SRTSTATUS srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->bstatsSocket(perf, clear); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { SetThreadLocalError(e); - return ERROR; + return SRT_ERROR; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "bstats: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return ERROR; + return SRT_ERROR; } } #endif @@ -4525,7 +4537,7 @@ int getrejectreason(SRTSOCKET u) return CUDT::rejectReason(u); } -int setrejectreason(SRTSOCKET u, int value) +SRTSTATUS setrejectreason(SRTSOCKET u, int value) { return CUDT::rejectReason(u, value); } diff --git a/srtcore/api.h b/srtcore/api.h index 551590e3e..9477ebb28 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -84,7 +84,7 @@ class CUDTSocket CUDTSocket() : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -103,7 +103,7 @@ class CUDTSocket CUDTSocket(const CUDTSocket& ancestor) : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -245,11 +245,11 @@ class CUDTUnited /// initialize the UDT library. /// @return 0 if success, otherwise -1 is returned. - int startup(); + SRTSTATUS startup(); /// release the UDT library. /// @return 0 if success, otherwise -1 is returned. - int cleanup(); + SRTSTATUS cleanup(); /// Create a new UDT socket. /// @param [out] pps Variable (optional) to which the new socket will be written, if succeeded @@ -273,8 +273,8 @@ class CUDTUnited 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); + SRTSTATUS installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + SRTSTATUS installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); /// Check the status of the UDT socket. /// @param [in] u the UDT socket ID. @@ -283,20 +283,20 @@ class CUDTUnited // socket APIs - int bind(CUDTSocket* u, const sockaddr_any& name); - int bind(CUDTSocket* u, UDPSOCKET udpsock); - int listen(const SRTSOCKET u, int backlog); + SRTSTATUS bind(CUDTSocket* u, const sockaddr_any& name); + SRTSTATUS bind(CUDTSocket* u, UDPSOCKET udpsock); + SRTSTATUS listen(const SRTSOCKET u, int backlog); SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen); SRTSOCKET accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); - int connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); - int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - int connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); + SRTSOCKET connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); + SRTSOCKET connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + void connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); #if ENABLE_BONDING - int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); - int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); + SRTSOCKET groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); + SRTSOCKET singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); #endif - int close(const SRTSOCKET u); - int close(CUDTSocket* s); + SRTSTATUS close(const SRTSOCKET u); + SRTSTATUS close(CUDTSocket* s); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); @@ -306,22 +306,22 @@ class CUDTUnited std::vector* exceptfds, int64_t msTimeOut); int epoll_create(); - int epoll_clear_usocks(int eid); - int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - int epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); - int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_remove_usock(const int eid, const SRTSOCKET u); + void epoll_clear_usocks(int eid); + void epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + void epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); + void epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + void epoll_remove_usock(const int eid, const SRTSOCKET u); template - int epoll_remove_entity(const int eid, EntityType* ent); - int epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); + void epoll_remove_entity(const int eid, EntityType* ent); + void epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); #if ENABLE_BONDING - int epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); + void epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); #endif - int epoll_remove_ssock(const int eid, const SYSSOCKET s); - int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); + void epoll_remove_ssock(const int eid, const SYSSOCKET s); + void epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); int32_t epoll_set(const int eid, int32_t flags); - int epoll_release(const int eid); + void epoll_release(const int eid); #if ENABLE_BONDING // [[using locked(m_GlobControlLock)]] @@ -396,8 +396,8 @@ class CUDTUnited 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 + int32_t m_SocketIDGenerator; // seed to generate a new unique socket ID + int32_t 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 diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a7056c3b2..dcc585a75 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -262,6 +262,10 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { + if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. + { + HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with mflag field set to a negative value!"); + } IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; int iDropCnt = 0; diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index e945b166c..7e55ecad4 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -320,7 +320,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -346,7 +346,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -421,9 +421,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, Drop& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -438,19 +439,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -462,11 +467,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -476,18 +481,21 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); + SRT_ASSERT(w_drop.seqno[Drop::END] == p->m_iSeqNo); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; @@ -500,7 +508,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -508,7 +516,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5dce96ae0..d0dcdd453 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct Drop + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. + /// @param [in,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). + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, Drop& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 30fd98778..4f752f290 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -613,8 +613,8 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) 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 - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f1f497ecb..3cf9fdaad 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -88,8 +88,8 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; -const int UDT::ERROR = srt::CUDT::ERROR; +const SRTSOCKET UDT::INVALID_SOCK = SRT_INVALID_SOCK; +const SRTSTATUS UDT::ERROR = SRT_ERROR; //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -272,7 +272,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_PeerID = SRT_SOCKID_CONNREQ; m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -1960,7 +1960,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2491,7 +2491,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2518,7 +2518,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3034,7 +3034,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // for consistency and possibly changes in future. // We are granted these two fields do exist - SRTSOCKET grpid = groupdata[GRPD_GROUPID]; + SRTSOCKET grpid = SRTSOCKET(groupdata[GRPD_GROUPID]); uint32_t gd = groupdata[GRPD_GROUPDATA]; SRT_GROUP_TYPE gtp = SRT_GROUP_TYPE(SrtHSRequest::HS_GROUP_TYPE::unwrap(gd)); @@ -3057,7 +3057,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A return false; } - if ((grpid & SRTGROUP_MASK) == 0) + if (!isgroup(grpid)) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS/GROUP: socket ID passed as a group ID is not a group ID"); @@ -3127,7 +3127,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A } SRTSOCKET peer = pg->peerid(); - if (peer == -1) + if (peer == SRT_INVALID_SOCK) { // This is the first connection within this group, so this group // has just been informed about the peer membership. Accept it. @@ -3169,10 +3169,10 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // ID to the peer. SRTSOCKET lgid = makeMePeerOf(grpid, gtp, link_flags); - if (!lgid) + if (lgid == SRT_SOCKID_CONNREQ) return true; // already done - if (lgid == -1) + if (lgid == SRT_INVALID_SOCK) { // NOTE: This error currently isn't reported by makeMePeerOf, // so this is left to handle a possible error introduced in future. @@ -3229,7 +3229,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp << " agent group=$" << gp->id() << " type" << gp->type()); - return -1; + return SRT_INVALID_SOCK; } HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id()); @@ -3246,14 +3246,14 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 catch (...) { // Expected exceptions are only those referring to system resources - return -1; + return SRT_INVALID_SOCK; } if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. uglobal().deleteGroup_LOCKED(gp); - return -1; + return SRT_INVALID_SOCK; } gp->set_peerid(peergroup); @@ -3297,7 +3297,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); s->m_GroupOf = gp; s->m_GroupMemberData = f; - return 0; + return SRT_SOCKID_CONNREQ; } s->m_GroupMemberData = gp->add(groups::prepareSocketData(s)); @@ -3520,7 +3520,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(SRT_SOCKID_CONNREQ); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3535,7 +3535,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3600,7 +3600,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3818,17 +3818,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? SRT_SOCKID_CONNREQ : m_ConnRes.m_iID); bool status = true; @@ -3839,7 +3839,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3871,8 +3871,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3884,7 +3884,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3894,16 +3894,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request); + m_pSndQueue->sendto(serv_addr, reqpkt); return status; } @@ -5674,15 +5674,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5693,10 +5693,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show()); } #endif @@ -5706,7 +5706,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } // This function is required to be called when a caller receives an INDUCTION @@ -5912,7 +5912,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -6740,7 +6740,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return 0; // Forced to return error instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0), int(SRT_ERROR); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } else @@ -6879,7 +6879,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { // Forced to return 0 instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0), int(SRT_ERROR); if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -7620,8 +7620,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + ctrlpkt.set_id(m_PeerID); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7635,7 +7635,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7656,7 +7656,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7686,7 +7686,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); m_tsLastWarningTime = steady_clock::now(); @@ -7695,37 +7695,37 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_SHUTDOWN: // 101 - Shutdown - if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. + if (m_PeerID == SRT_SOCKID_CONNREQ) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7812,7 +7812,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8019,7 +8019,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8711,18 +8711,18 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) + (rsppkt), (initdata))) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -8737,6 +8737,27 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { + typedef int32_t expected_t[2]; + if (ctrlpkt.getLength() < sizeof (expected_t)) + { + // We ALLOW packets that are bigger than this to allow + // future extensions, this just interprets the part that + // is expected, and reject only those that don't carry + // even the required data. + LOGC(brlog.Error, log << CONID() << "EPE: Wrong size of the DROPREQ message: " << ctrlpkt.getLength() + << " - expected >=" << sizeof(expected_t)); + return; + } + + int32_t msgno = ctrlpkt.getMsgSeq(m_bPeerRexmitFlag); + + // Check for rogue message + if (msgno == SRT_MSGNO_NONE) + { + LOGC(brlog.Warn, log << CONID() << "ROGUE DROPREQ detected with #NONE - fallback: fixing to #CONTROL"); + msgno = SRT_MSGNO_CONTROL; + } + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; { @@ -8747,15 +8768,13 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // 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); - const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], msgno); 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)."); + << dropdata[0] << "-%" << dropdata[1] << ", #" << msgno << " (SND DROP REQUEST)."); enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. @@ -8844,7 +8863,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=@" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9019,37 +9038,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9059,35 +9085,32 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; } } - int msglen; + CSndBuffer::Drop buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[CSndBuffer::Drop::BEGIN], buffer_drop.seqno[CSndBuffer::Drop::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[CSndBuffer::Drop::BEGIN] << " - " + << buffer_drop.seqno[CSndBuffer::Drop::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[CSndBuffer::Drop::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[CSndBuffer::Drop::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored @@ -9112,7 +9135,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9213,7 +9236,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9225,14 +9248,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9241,7 +9264,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9338,14 +9361,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9355,7 +9378,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9374,7 +9397,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9460,7 +9483,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9474,7 +9497,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9482,7 +9505,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9491,7 +9514,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9502,17 +9525,17 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // packet are not present in the buffer (preadte the send buffer). // Override extraction sequence with scheduling sequence. - m_iSndCurrSeqNo = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); ScopedLock ackguard(m_RecvAckLock); - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << m_iSndCurrSeqNo << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9524,15 +9547,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = m_iSndCurrSeqNo; + w_packet.set_seqno(m_iSndCurrSeqNo); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9552,7 +9575,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -9723,7 +9746,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -9748,7 +9771,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -9761,7 +9784,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -9783,7 +9806,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -9794,7 +9817,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -9885,7 +9908,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -9899,12 +9922,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -9912,9 +9935,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -9962,7 +9985,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10006,7 +10029,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10032,7 +10055,7 @@ int srt::CUDT::processData(CUnit* in_unit) // supply the missing packet(s), and the loss will no longer be visible for the code that follows. if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing { - const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10048,13 +10071,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10120,7 +10143,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10405,7 +10428,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10425,7 +10448,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10721,7 +10744,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -10789,7 +10812,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -10837,8 +10860,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } @@ -10949,7 +10972,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp); } else @@ -10960,7 +10983,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); @@ -11448,7 +11471,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u) return s->core().m_RejectReason; } -int srt::CUDT::rejectReason(SRTSOCKET u, int value) +SRTSTATUS srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = uglobal().locateSocket(u); if (!s) @@ -11458,7 +11481,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u, int value) return APIError(MJ_NOTSUP, MN_INVAL); s->core().m_RejectReason = value; - return 0; + return SRT_STATUS_OK; } int64_t srt::CUDT::socketStartTime(SRTSOCKET u) diff --git a/srtcore/core.h b/srtcore/core.h index 64e02ef11..7f8d7cb8d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -186,30 +186,30 @@ class CUDT ~CUDT(); public: //API - static int startup(); - static int cleanup(); + static SRTSTATUS startup(); + static SRTSTATUS cleanup(); static SRTSOCKET socket(); #if ENABLE_BONDING static SRTSOCKET createGroup(SRT_GROUP_TYPE); static SRTSOCKET getGroupOfSocket(SRTSOCKET socket); static int getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize); - static bool isgroup(SRTSOCKET sock) { return (sock & SRTGROUP_MASK) != 0; } + static bool isgroup(SRTSOCKET sock) { return (int32_t(sock) & SRTGROUP_MASK) != 0; } #endif - static int bind(SRTSOCKET u, const sockaddr* name, int namelen); - static int bind(SRTSOCKET u, UDPSOCKET udpsock); - static int listen(SRTSOCKET u, int backlog); + static SRTSTATUS bind(SRTSOCKET u, const sockaddr* name, int namelen); + static SRTSTATUS bind(SRTSOCKET u, UDPSOCKET udpsock); + static SRTSTATUS listen(SRTSOCKET u, int backlog); static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen); static SRTSOCKET accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut); - static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - static int connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); #if ENABLE_BONDING - static int connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); + static SRTSOCKET connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); #endif - static int close(SRTSOCKET u); - static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); - static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); + static SRTSTATUS close(SRTSOCKET u); + static SRTSTATUS getpeername(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockname(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); + static SRTSTATUS setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); static int send(SRTSOCKET u, const char* buf, int len, int flags); static int recv(SRTSOCKET u, char* buf, int len, int flags); static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = SRT_MSGTTL_INF, bool inorder = false, int64_t srctime = 0); @@ -221,29 +221,29 @@ class CUDT static int select(int nfds, UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); static int epoll_create(); - static int epoll_clear_usocks(int eid); - static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - static int epoll_remove_usock(const int eid, const SRTSOCKET u); - static int epoll_remove_ssock(const int eid, const SYSSOCKET s); - static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_clear_usocks(int eid); + static SRTSTATUS epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_remove_usock(const int eid, const SRTSOCKET u); + static SRTSTATUS epoll_remove_ssock(const int eid, const SYSSOCKET s); + static SRTSTATUS epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); static int32_t epoll_set(const int eid, int32_t flags); - static int epoll_release(const int eid); + static SRTSTATUS epoll_release(const int eid); static CUDTException& getlasterror(); - static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); + static SRTSTATUS bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); #if ENABLE_BONDING - static int groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); + static SRTSTATUS groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); #endif static SRT_SOCKSTATUS getsockstate(SRTSOCKET u); static bool setstreamid(SRTSOCKET u, const std::string& sid); static std::string getstreamid(SRTSOCKET u); - static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); + static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); // returns buffer span in [ms] static int rejectReason(SRTSOCKET s); - static int rejectReason(SRTSOCKET s, int value); + static SRTSTATUS rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); public: // internal API @@ -252,16 +252,15 @@ class CUDT { APIError(const CUDTException&); APIError(CodeMajor, CodeMinor, int = 0); + APIError(int error_code); + // This represents both SRT_ERROR and SRT_INVALID_SOCK. operator int() const { return SRT_ERROR; } }; - 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; @@ -938,8 +937,8 @@ class CUDT // FORWARDER public: - static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); - static int installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); + static SRTSTATUS installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + static SRTSTATUS installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); private: void installAcceptHook(srt_listen_callback_fn* hook, void* opaq) { diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 866ec2fa7..d4c22d69d 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -695,7 +695,7 @@ void srt::CCryptoControl::close() std::string srt::CCryptoControl::CONID() const { - if (m_SocketID == 0) + if (int32_t(m_SocketID) <= 0) return ""; std::ostringstream os; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 4040a21be..0117a95e2 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -165,7 +165,7 @@ ENOMEM: There was insufficient memory to create the kernel object. return m_iIDSeed; } -int srt::CEPoll::clear_usocks(int eid) +void srt::CEPoll::clear_usocks(int eid) { // This should remove all SRT sockets from given eid. ScopedLock pg (m_EPollLock); @@ -177,8 +177,6 @@ int srt::CEPoll::clear_usocks(int eid) CEPollDesc& d = p->second; d.clearAll(); - - return 0; } @@ -219,7 +217,7 @@ void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction) d.removeSubscription(cleared[i]); } -int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -287,11 +285,9 @@ int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) #endif p->second.m_sLocals.insert(s); - - return 0; } -int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +void srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) { ScopedLock pg(m_EPollLock); @@ -317,12 +313,10 @@ int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) #endif p->second.m_sLocals.erase(s); - - return 0; } // Need this to atomically modify polled events (ex: remove write/keep read) -int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) +void srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) { ScopedLock pg(m_EPollLock); IF_HEAVY_LOGGING(ostringstream evd); @@ -391,10 +385,9 @@ int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* even HLOGC(ealog.Debug, log << "srt_epoll_update_usock: REMOVED E" << eid << " socket @" << u); d.removeSubscription(u); } - return 0; } -int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -462,10 +455,9 @@ int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* even // Assuming add is used if not inserted // p->second.m_sLocals.insert(s); - return 0; } -int srt::CEPoll::setflags(const int eid, int32_t flags) +int32_t srt::CEPoll::setflags(const int eid, int32_t flags) { ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); @@ -845,7 +837,7 @@ bool srt::CEPoll::empty(const CEPollDesc& d) const return d.watch_empty(); } -int srt::CEPoll::release(const int eid) +void srt::CEPoll::release(const int eid) { ScopedLock pg(m_EPollLock); @@ -861,8 +853,6 @@ int srt::CEPoll::release(const int eid) #endif m_mPolls.erase(i); - - return 0; } diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 7b0d941c8..d0ccec25d 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -374,38 +374,29 @@ friend class srt::CRendezvousQueue; /// delete all user sockets (SRT sockets) from an EPoll /// @param [in] eid EPoll ID. - /// @return 0 - int clear_usocks(int eid); + void clear_usocks(int eid); /// add a system socket to an EPoll. /// @param [in] eid EPoll ID. /// @param [in] s system Socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// remove a system socket event from an EPoll; socket will be removed if no events to watch. /// @param [in] eid EPoll ID. /// @param [in] s system socket ID. - /// @return 0 if success, otherwise an error number. - - int remove_ssock(const int eid, const SYSSOCKET& s); + void remove_ssock(const int eid, const SYSSOCKET& s); /// update a UDT socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_usock(const int eid, const SRTSOCKET& u, const int* events); + void update_usock(const int eid, const SRTSOCKET& u, const int* events); /// update a system socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// wait for EPoll events or timeout. /// @param [in] eid EPoll ID. @@ -472,7 +463,7 @@ friend class srt::CRendezvousQueue; /// @param [in] eid EPoll ID. /// @return 0 if success, otherwise an error number. - int release(const int eid); + void release(const int eid); public: // for CUDT to acknowledge IO status @@ -486,7 +477,7 @@ friend class srt::CRendezvousQueue; int update_events(const SRTSOCKET& uid, std::set& eids, int events, bool enable); - int setflags(const int eid, int32_t flags); + int32_t setflags(const int eid, int32_t flags); private: int m_iIDSeed; // seed to generate a new ID diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 541636b44..e7e50cb91 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -1443,7 +1443,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) ; p.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; - p.hdr[SRT_PH_ID] = rcv.id; + p.hdr[SRT_PH_ID] = int32_t(rcv.id); // Header ready, now we rebuild the contents // First, rebuild the length. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 572e0d746..766eabc26 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -249,8 +249,8 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_Global(CUDT::uglobal()) - , m_GroupID(-1) - , m_PeerGroupID(-1) + , m_GroupID(SRT_INVALID_SOCK) + , m_PeerGroupID(SRT_INVALID_SOCK) , m_type(gtype) , m_listener() , m_iBusy() @@ -913,7 +913,7 @@ void CUDTGroup::close() // removing themselves from the group when closing because they // are unaware of being group members. m_Group.clear(); - m_PeerGroupID = -1; + m_PeerGroupID = SRT_INVALID_SOCK; set epollid; { diff --git a/srtcore/group.h b/srtcore/group.h index 09e072267..99826e333 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -757,7 +757,7 @@ class CUDTGroup { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_GroupID << ":"; + os << "$" << m_GroupID << ":"; return os.str(); #else return ""; diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index f8f03c84d..63854b9ad 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -88,7 +88,7 @@ int srt::CHandShake::store_to(char* buf, size_t& w_size) *p++ = m_iMSS; *p++ = m_iFlightFlagSize; *p++ = int32_t(m_iReqType); - *p++ = m_iID; + *p++ = int32_t(m_iID); *p++ = m_iCookie; for (int i = 0; i < 4; ++ i) *p++ = m_piPeerIP[i]; @@ -111,7 +111,7 @@ int srt::CHandShake::load_from(const char* buf, size_t size) m_iMSS = *p++; m_iFlightFlagSize = *p++; m_iReqType = UDTRequestType(*p++); - m_iID = *p++; + m_iID = SRTSOCKET(*p++); m_iCookie = *p++; for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = *p++; diff --git a/srtcore/handshake.h b/srtcore/handshake.h index 93a351f39..a505abf7a 100644 --- a/srtcore/handshake.h +++ b/srtcore/handshake.h @@ -318,7 +318,7 @@ class CHandShake int32_t m_iMSS; // maximum segment size int32_t m_iFlightFlagSize; // flow control window size UDTRequestType m_iReqType; // handshake stage - int32_t m_iID; // SRT socket ID of HS sender + SRTSOCKET m_iID; // SRT socket ID of HS sender int32_t m_iCookie; // cookie uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index bf5db8c0d..a855552cd 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -600,7 +596,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index e6d2516a9..4f17d4df4 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -349,12 +349,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1b05c4f4e..e7a9ca2bb 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 1bdff1848..7cbe928ae 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -731,10 +731,10 @@ void srt::CHash::init(int size) m_iHashSize = size; } -srt::CUDT* srt::CHash::lookup(int32_t id) +srt::CUDT* srt::CHash::lookup(SRTSOCKET id) { // simple hash function (% hash table size); suitable for socket descriptors - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); while (NULL != b) { @@ -746,21 +746,21 @@ srt::CUDT* srt::CHash::lookup(int32_t id) return NULL; } -void srt::CHash::insert(int32_t id, CUDT* u) +void srt::CHash::insert(SRTSOCKET id, CUDT* u) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* n = new CBucket; n->m_iID = id; n->m_pUDT = u; n->m_pNext = b; - m_pBucket[id % m_iHashSize] = n; + bucketAt(id) = n; } -void srt::CHash::remove(int32_t id) +void srt::CHash::remove(SRTSOCKET id) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* p = NULL; while (NULL != b) @@ -768,7 +768,7 @@ void srt::CHash::remove(int32_t id) if (id == b->m_iID) { if (NULL == p) - m_pBucket[id % m_iHashSize] = b->m_pNext; + bucketAt(id) = b->m_pNext; else p->m_pNext = b->m_pNext; @@ -831,12 +831,12 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& { ScopedLock vg(m_RIDListLock); - IF_HEAVY_LOGGING(const char* const id_type = w_id ? "THIS ID" : "A NEW CONNECTION"); + IF_HEAVY_LOGGING(const char* const id_type = w_id == SRT_SOCKID_CONNREQ ? "A NEW CONNECTION" : "THIS ID" ); // 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))) + if (i->m_PeerAddr == addr && ((w_id == SRT_SOCKID_CONNREQ) || (w_id == i->m_iID))) { // This procedure doesn't exactly respond to the original UDT idea. // As the "rendezvous queue" is used for both handling rendezvous and @@ -855,7 +855,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& // This means: if an incoming ID is 0, then this search should succeed ONLY // IF THE FOUND SOCKET WAS RENDEZVOUS. - if (!w_id && !i->m_pUDT->m_config.bRendezvous) + if (w_id == SRT_SOCKID_CONNREQ && !i->m_pUDT->m_config.bRendezvous) { HLOGC(cnlog.Debug, log << "RID: found id @" << i->m_iID << " while looking for " @@ -874,7 +874,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& #if ENABLE_HEAVY_LOGGING std::ostringstream spec; - if (w_id == 0) + if (w_id == SRT_SOCKID_CONNREQ) spec << "A NEW CONNECTION REQUEST"; else spec << " AGENT @" << w_id; @@ -894,7 +894,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : SRT_SOCKID_CONNREQ; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -925,7 +925,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (cst != CONN_RENDEZVOUS && dest_id != 0) + if (cst != CONN_RENDEZVOUS && dest_id != SRT_SOCKID_CONNREQ) { if (i->id != dest_id) { @@ -1011,7 +1011,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, - int iDstSockID, + SRTSOCKET iDstSockID, vector& toRemove, vector& toProcess) { @@ -1151,7 +1151,7 @@ srt::CRcvQueue::~CRcvQueue() delete m_pRendezvousQueue; // remove all queued messages - for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + for (qmap_t::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) { while (!i->second.empty()) { @@ -1201,7 +1201,7 @@ void* srt::CRcvQueue::worker(void* param) { CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->getIPversion()); - int32_t id = 0; + SRTSOCKET id = SRT_SOCKID_CONNREQ; #if ENABLE_LOGGING THREAD_STATE_INIT(("SRT:RcvQ:w" + Sprint(m_counter)).c_str()); @@ -1217,7 +1217,7 @@ void* srt::CRcvQueue::worker(void* param) EReadStatus rst = self->worker_RetrieveUnit((id), (unit), (sa)); if (rst == RST_OK) { - if (id < 0) + if (id < 0) // Any negative (illegal range) and SRT_INVALID_SOCKET { // User error on peer. May log something, but generally can only ignore it. // XXX Think maybe about sending some "connection rejection response". @@ -1234,7 +1234,7 @@ void* srt::CRcvQueue::worker(void* param) // Note to rendezvous connection. This can accept: // - ID == 0 - take the first waiting rendezvous socket // - ID > 0 - find the rendezvous socket that has this ID. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets cst = self->worker_ProcessConnectionRequest(unit, sa); @@ -1330,7 +1330,7 @@ void* srt::CRcvQueue::worker(void* param) return NULL; } -srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) +srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(SRTSOCKET& 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 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); @@ -1428,10 +1428,12 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, } // If there's no listener waiting for the packet, just store it into the queue. - return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. + // Passing SRT_SOCKID_CONNREQ explicitly because it's a handler for a HS packet + // that came for this very ID. + return worker_TryAsyncRend_OrStore(SRT_SOCKID_CONNREQ, unit, addr); } -srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); if (!u) @@ -1483,7 +1485,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CU // 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. -srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(SRTSOCKET 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) @@ -1504,7 +1506,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni // not belonging to the connection and not registered as rendezvous) as "possible // attack" and ignore it. This also should better protect the rendezvous socket // against a rogue connector. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { HLOGC(cnlog.Debug, log << CONID() << "AsyncOrRND: no sockets expect connection from " << addr.str() @@ -1630,11 +1632,11 @@ void srt::CRcvQueue::stopWorker() m_WorkerThread.join(); } -int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) +int srt::CRcvQueue::recvfrom(SRTSOCKET id, CPacket& w_packet) { CUniqueSync buffercond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1719,7 +1721,7 @@ void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) ScopedLock bufferlock(m_BufferLock); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { HLOGC(cnlog.Debug, @@ -1759,11 +1761,11 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } -void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) +void srt::CRcvQueue::storePkt(SRTSOCKET id, CPacket* pkt) { CUniqueSync passcond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { diff --git a/srtcore/queue.h b/srtcore/queue.h index 208b23058..8b25b2d0c 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -279,23 +279,23 @@ class CHash /// @param [in] id socket ID /// @return Pointer to a UDT instance, or NULL if not found. - CUDT* lookup(int32_t id); + CUDT* lookup(SRTSOCKET id); /// 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(SRTSOCKET id, CUDT* u); /// Remove an entry from the hash table. /// @param [in] id socket ID - void remove(int32_t id); + void remove(SRTSOCKET id); private: struct CBucket { - int32_t m_iID; // Socket ID + SRTSOCKET m_iID; // Socket ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -303,6 +303,11 @@ class CHash int m_iHashSize; // size of hash table + CBucket*& bucketAt(SRTSOCKET id) + { + return m_pBucket[int32_t(id) % m_iHashSize]; + } + private: CHash(const CHash&); CHash& operator=(const CHash&); @@ -374,7 +379,7 @@ class CRendezvousQueue /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, - int iDstSockID, + SRTSOCKET iDstSockID, std::vector& toRemove, std::vector& toProcess); @@ -505,7 +510,7 @@ class CRcvQueue /// @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(SRTSOCKET id, CPacket& to_packet); void stopWorker(); @@ -517,10 +522,10 @@ class CRcvQueue static void* worker(void* param); sync::CThread m_WorkerThread; // Subroutines of worker - EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); + EReadStatus worker_RetrieveUnit(SRTSOCKET& 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); + EConnectStatus worker_TryAsyncRend_OrStore(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); private: CUnitQueue* m_pUnitQueue; // The received packet queue @@ -551,7 +556,7 @@ class CRcvQueue bool ifNewEntry(); CUDT* getNewEntry(); - void storePkt(int32_t id, CPacket* pkt); + void storePkt(SRTSOCKET id, CPacket* pkt); private: sync::Mutex m_LSLock; @@ -561,9 +566,10 @@ class CRcvQueue std::vector m_vNewEntry; // newly added entries, to be inserted sync::Mutex m_IDLock; - std::map > m_mBuffer; // temporary buffer for rendezvous connection request - sync::Mutex m_BufferLock; - sync::Condition m_BufferCond; + typedef std::map > qmap_t; + qmap_t m_mBuffer; // temporary buffer for rendezvous connection request + sync::Mutex m_BufferLock; + sync::Condition m_BufferCond; private: CRcvQueue(const CRcvQueue&); diff --git a/srtcore/srt.h b/srtcore/srt.h index 4bdbdf4b6..5b49e5e36 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -139,6 +139,7 @@ extern "C" { #endif typedef int32_t SRTSOCKET; +typedef int SRTSTATUS; // The most significant bit 31 (sign bit actually) is left unused, // so that all people who check the value for < 0 instead of -1 @@ -148,6 +149,12 @@ typedef int32_t SRTSOCKET; // socket or a socket group. static const int32_t SRTGROUP_MASK = (1 << 30); +#ifdef __cplusplus +namespace srt { +inline bool isgroup(SRTSOCKET sid) { return (int32_t(sid) & SRTGROUP_MASK) != 0; } +} +#endif + #ifdef _WIN32 typedef SOCKET SYSSOCKET; #else @@ -741,11 +748,13 @@ inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2) typedef struct CBytePerfMon SRT_TRACEBSTATS; static const SRTSOCKET SRT_INVALID_SOCK = -1; -static const int SRT_ERROR = -1; +static const SRTSOCKET SRT_SOCKID_CONNREQ = 0; +static const SRTSTATUS SRT_ERROR = -1; +static const SRTSTATUS SRT_STATUS_OK = 0; // library initialization -SRT_API int srt_startup(void); -SRT_API int srt_cleanup(void); +SRT_API SRTSTATUS srt_startup(void); +SRT_API SRTSTATUS srt_cleanup(void); // // Socket operations @@ -756,33 +765,33 @@ SRT_API int srt_cleanup(void); SRT_ATR_DEPRECATED_PX SRT_API SRTSOCKET srt_socket(int, int, int) SRT_ATR_DEPRECATED; SRT_API SRTSOCKET srt_create_socket(void); -SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); +SRT_API SRTSTATUS srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSTATUS srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); // Old name of srt_bind_acquire(), please don't use // Planned deprecation removal: rel1.6.0 -SRT_ATR_DEPRECATED_PX static inline int srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; -static inline int srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } -SRT_API int srt_listen (SRTSOCKET u, int backlog); +SRT_ATR_DEPRECATED_PX static inline SRTSTATUS srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; +static inline SRTSTATUS srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } +SRT_API SRTSTATUS srt_listen (SRTSOCKET u, int backlog); SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen); SRT_API SRTSOCKET srt_accept_bond (const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid); -SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); typedef void srt_connect_callback_fn (void* opaq, SRTSOCKET ns, int errorcode, const struct sockaddr* peeraddr, int token); -SRT_API int srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); -SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); -SRT_API int srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, +SRT_API SRTSTATUS srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSOCKET srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRT_API SRTSOCKET srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); -SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRT_API SRTSTATUS srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); -SRT_API int srt_close (SRTSOCKET u); -SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); -SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); -SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); -SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API SRTSTATUS srt_close (SRTSOCKET u); +SRT_API SRTSTATUS srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); +SRT_API SRTSTATUS srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); +SRT_API SRTSTATUS srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); +SRT_API SRTSTATUS srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; @@ -834,18 +843,18 @@ SRT_API extern const SRT_MSGCTRL srt_msgctrl_default; // // Sending functions // -SRT_API int srt_send (SRTSOCKET u, const char* buf, int len); -SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/); -SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); +SRT_API int srt_send (SRTSOCKET u, const char* buf, int len); +SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/); +SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); // // Receiving functions // -SRT_API int srt_recv (SRTSOCKET u, char* buf, int len); +SRT_API int srt_recv (SRTSOCKET u, char* buf, int len); // srt_recvmsg is actually an alias to srt_recv, it stays under the old name for compat reasons. -SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len); -SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl); +SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len); +SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl); // Special send/receive functions for files only. @@ -857,29 +866,29 @@ SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int // last error detection SRT_API const char* srt_getlasterror_str(void); -SRT_API int srt_getlasterror(int* errno_loc); +SRT_API int srt_getlasterror(int* errno_loc); SRT_API const char* srt_strerror(int code, int errnoval); SRT_API void srt_clearlasterror(void); // Performance tracking // Performance monitor with Byte counters for better bitrate estimation. -SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRT_API SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRT_API SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); // Socket Status (for problem tracking) SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u); -SRT_API int srt_epoll_create(void); -SRT_API int srt_epoll_clear_usocks(int eid); -SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u); -SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s); -SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API int srt_epoll_create(void); +SRT_API SRTSTATUS srt_epoll_clear_usocks(int eid); +SRT_API SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRT_API SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRT_API SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); -SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut, +SRT_API 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); typedef struct SRT_EPOLL_EVENT_STR { @@ -887,13 +896,13 @@ 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(): fd(-1), events(0) {} // NOTE: allows singular values, no init. + SRT_EPOLL_EVENT_STR(): fd(SRT_INVALID_SOCK), 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); +SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); SRT_API int32_t srt_epoll_set(int eid, int32_t flags); -SRT_API int srt_epoll_release(int eid); +SRT_API SRTSTATUS srt_epoll_release(int eid); // Logging control @@ -908,10 +917,10 @@ SRT_API void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler); SRT_API void srt_setlogflags(int flags); -SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); +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 int srt_getrejectreason(SRTSOCKET sock); +SRT_API SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); // The srt_rejectreason_msg[] array is deprecated (as unsafe). // Planned removal: v1.6.0. SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg []; @@ -984,14 +993,14 @@ typedef struct SRT_GroupMemberConfig_ SRT_API SRTSOCKET srt_create_group(SRT_GROUP_TYPE); SRT_API SRTSOCKET srt_groupof(SRTSOCKET socket); -SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); +SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); SRT_API SRT_SOCKOPT_CONFIG* srt_create_config(void); SRT_API void srt_delete_config(SRT_SOCKOPT_CONFIG* config /*nullable*/); -SRT_API int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); +SRT_API SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); SRT_API SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, const struct sockaddr* adr, int namelen); -SRT_API int srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); +SRT_API SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); #ifdef __cplusplus } diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 2b5f3d72f..430205b37 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -29,8 +29,8 @@ using namespace srt; extern "C" { -int srt_startup() { return CUDT::startup(); } -int srt_cleanup() { return CUDT::cleanup(); } +SRTSTATUS srt_startup() { return CUDT::startup(); } +SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } // Socket creation. SRTSOCKET srt_socket(int , int , int ) { return CUDT::socket(); } @@ -50,18 +50,18 @@ SRT_SOCKOPT_CONFIG* srt_create_config() return new SRT_SocketOptionObject; } -int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) { if (!config) - return -1; + return SRT_ERROR; if (!config->add(option, contents, len)) - return -1; + return SRT_ERROR; - return 0; + return SRT_STATUS_OK; } -int srt_connect_group(SRTSOCKET group, +SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize) { return CUDT::connectLinks(group, name, arraysize); @@ -71,11 +71,11 @@ int srt_connect_group(SRTSOCKET group, SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return SRT_INVALID_SOCK; } SRTSOCKET srt_groupof(SRTSOCKET) { return SRT_INVALID_SOCK; } -int srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } -int srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } -int srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } #endif @@ -87,7 +87,7 @@ SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struc #else data.errorcode = SRT_EINVOP; #endif - data.id = -1; + data.id = SRT_INVALID_SOCK; data.token = -1; data.weight = 0; data.config = NULL; @@ -109,21 +109,21 @@ void srt_delete_config(SRT_SOCKOPT_CONFIG* in) } // Binding and connection management -int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } -int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } +SRTSTATUS srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); } SRTSOCKET srt_accept_bond(const SRTSOCKET lsns[], int lsize, int64_t msTimeOut) { return CUDT::accept_bond(lsns, lsize, msTimeOut); } -int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } -int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } -int srt_connect_bind(SRTSOCKET u, +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int target_len) { return CUDT::connect(u, source, target, target_len); } -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { bool yes = 1; @@ -135,14 +135,20 @@ int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_nam || local_name->sa_family != remote_name->sa_family) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); - const int st = srt_bind(u, local_name, local_namelen); - if (st != 0) + const SRTSTATUS st = srt_bind(u, local_name, local_namelen); + if (st != SRT_STATUS_OK) return st; - return srt_connect(u, remote_name, remote_namelen); + // Note: srt_connect may potentially return a socket value if it is used + // to connect a group. But rendezvous is not supported for groups. + const SRTSOCKET sst = srt_connect(u, remote_name, remote_namelen); + if (sst == SRT_INVALID_SOCK) + return SRT_ERROR; + + return SRT_STATUS_OK; } -int srt_close(SRTSOCKET u) +SRTSTATUS srt_close(SRTSOCKET u) { SRT_SOCKSTATUS st = srt_getsockstate(u); @@ -151,22 +157,22 @@ int srt_close(SRTSOCKET u) (st == SRTS_CLOSING) ) { // It's closed already. Do nothing. - return 0; + return SRT_STATUS_OK; } return CUDT::close(u); } -int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } -int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } -int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) { return CUDT::getsockopt(u, level, optname, optval, optlen); } -int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) { return CUDT::setsockopt(u, level, optname, optval, optlen); } -int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) +SRTSTATUS srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) { return CUDT::getsockopt(u, 0, opt, optval, optlen); } -int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } @@ -261,21 +267,21 @@ void srt_clearlasterror() UDT::getlasterror().clear(); } -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::getsockstate(u)); } // event mechanism int srt_epoll_create() { return CUDT::epoll_create(); } -int srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } +SRTSTATUS srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } // You can use either SRT_EPOLL_* flags or EPOLL* flags from , both are the same. IN/OUT/ERR only. // events == NULL accepted, in which case all flags are set. -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -289,15 +295,15 @@ int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) return CUDT::epoll_add_ssock(eid, s, &flag); } -int srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } -int srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_update_usock(eid, u, events); } -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -338,7 +344,7 @@ int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTim // Pass -1 to not change anything (but still get the current flag value). int32_t srt_epoll_set(int eid, int32_t flags) { return CUDT::epoll_set(eid, flags); } -int srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } +SRTSTATUS srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } void srt_setloglevel(int ll) { @@ -380,12 +386,12 @@ int srt_getrejectreason(SRTSOCKET sock) return CUDT::rejectReason(sock); } -int srt_setrejectreason(SRTSOCKET sock, int value) +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value) { return CUDT::rejectReason(sock, value); } -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { if (!hook) return CUDT::APIError(MJ_NOTSUP, MN_INVAL); @@ -393,7 +399,7 @@ int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) return CUDT::installAcceptHook(lsn, hook, opaq); } -int srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { if (!hook) return CUDT::APIError(MJ_NOTSUP, MN_INVAL); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 046c90b74..23c67badd 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -162,7 +162,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra m_bTsbPdWrapCheck = wrap; // Timebase passed here comes is calculated as: - // Tnow - hspkt.m_iTimeStamp + // Tnow - hspkt.timestamp() // where hspkt is the packet with SRT_CMD_HSREQ message. // // This function is called in the HSREQ reception handler only. diff --git a/srtcore/udt.h b/srtcore/udt.h index 22f6be870..d0e1afac1 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -166,7 +166,7 @@ typedef std::set UDSET; SRT_API extern const SRTSOCKET INVALID_SOCK; #undef ERROR -SRT_API extern const int ERROR; +SRT_API extern const SRTSTATUS ERROR; SRT_API int startup(); SRT_API int cleanup(); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 31e05b205..a9f3bdba1 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1089,6 +1089,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index ecc4a4947..a9dba46ba 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -236,7 +236,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -257,7 +257,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -267,7 +267,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -282,7 +282,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 15356c8f2..db0db317a 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,21 +54,26 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + } + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index 75386bcb9..edaac17f1 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -86,9 +86,9 @@ namespace srt const int inorder = 1; const int kflg = m_crypt.getSndCryptoFlags(); - pkt.m_iSeqNo = seqno; - pkt.m_iMsgNo = msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg);; - pkt.m_iTimeStamp = 356; + pkt.set_seqno(seqno); + pkt.set_msgflags(msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg)); + pkt.set_timestamp(356); std::iota(pkt.data(), pkt.data() + pld_size, '0'); pkt.setLength(pld_size); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index f9dfc393c..5d4d423da 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -222,7 +222,7 @@ TEST(CEPoll, HandleEpollEvent) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -238,7 +238,7 @@ TEST(CEPoll, HandleEpollEvent) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -248,7 +248,7 @@ TEST(CEPoll, HandleEpollEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -399,7 +399,7 @@ TEST(CEPoll, HandleEpollEvent2) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR | SRT_EPOLL_ET; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -420,7 +420,7 @@ TEST(CEPoll, HandleEpollEvent2) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -430,7 +430,7 @@ TEST(CEPoll, HandleEpollEvent2) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -461,7 +461,7 @@ TEST(CEPoll, HandleEpollNoEvent) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); SRT_EPOLL_EVENT fds[1024]; @@ -472,7 +472,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -482,7 +482,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -515,7 +515,7 @@ TEST(CEPoll, ThreadedUpdate) this_thread::sleep_for(chrono::seconds(1)); // Make sure that uwait will be called as first cerr << "ADDING sockets to eid\n"; const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -539,7 +539,7 @@ TEST(CEPoll, ThreadedUpdate) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -549,7 +549,7 @@ TEST(CEPoll, ThreadedUpdate) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..91d269221 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -792,7 +792,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -869,7 +869,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -887,7 +887,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b07f92ae8..f28aef811 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -471,10 +471,10 @@ void SrtCommon::InitParameters(string host, string path, map par) void SrtCommon::PrepareListener(string host, int port, int backlog) { m_bindsock = srt_create_socket(); - if (m_bindsock == SRT_ERROR) + if (m_bindsock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_bindsock); + SRTSTATUS stat = ConfigurePre(m_bindsock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -529,7 +529,7 @@ void SrtCommon::AcceptNewClient() int len = 2; SRTSOCKET ready[2]; - while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == -1) + while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == SRT_ERROR) { if (::transmit_int_state) Error("srt_epoll_wait for srt_accept: interrupt"); @@ -552,7 +552,7 @@ void SrtCommon::AcceptNewClient() } #if ENABLE_BONDING - if (m_sock & SRTGROUP_MASK) + if (srt::isgroup(m_sock)) { m_listener_group = true; if (m_group_config != "") @@ -565,7 +565,7 @@ void SrtCommon::AcceptNewClient() #ifndef SRT_OLD_APP_READER - if (srt_epoll != -1) + if (srt_epoll != SRT_ERROR) { Verb() << "(Group: erasing epoll " << srt_epoll << ") " << VerbNoEOL; srt_epoll_release(srt_epoll); @@ -588,14 +588,14 @@ void SrtCommon::AcceptNewClient() { sockaddr_any peeraddr(AF_INET6); string peer = ""; - if (-1 != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) + if (SRT_ERROR != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) { peer = peeraddr.str(); } sockaddr_any agentaddr(AF_INET6); string agent = ""; - if (-1 != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) + if (SRT_ERROR != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) { agent = agentaddr.str(); } @@ -606,7 +606,7 @@ void SrtCommon::AcceptNewClient() // ConfigurePre is done on bindsock, so any possible Pre flags // are DERIVED by sock. ConfigurePost is done exclusively on sock. - int stat = ConfigurePost(m_sock); + SRTSTATUS stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) Error("ConfigurePost"); } @@ -731,7 +731,7 @@ void SrtCommon::Init(string host, int port, string path, map par, if (!m_blocking_mode) { // Don't add new epoll if already created as a part - // of group management: if (srt_epoll == -1)... + // of group management: if (srt_epoll == SRT_ERROR)... if (m_mode == "caller") dir = (dir | SRT_EPOLL_UPDATE); @@ -744,7 +744,7 @@ void SrtCommon::Init(string host, int port, string path, map par, int SrtCommon::AddPoller(SRTSOCKET socket, int modes) { int pollid = srt_epoll_create(); - if (pollid == -1) + if (pollid == SRT_ERROR) throw std::runtime_error("Can't create epoll in nonblocking mode"); Verb() << "EPOLL: creating eid=" << pollid << " and adding @" << socket << " in " << DirectionName(SRT_EPOLL_OPT(modes)) << " mode"; @@ -752,15 +752,15 @@ int SrtCommon::AddPoller(SRTSOCKET socket, int modes) return pollid; } -int SrtCommon::ConfigurePost(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePost(SRTSOCKET sock) { bool yes = m_blocking_mode; - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; if (m_direction & SRT_EPOLL_OUT) { Verb() << "Setting SND blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -771,7 +771,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) if (m_timeout) result = srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -785,7 +785,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) { Verb() << "Setting RCV blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; if (m_timeout) @@ -795,7 +795,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) int timeout = 1000; result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout); } - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -816,18 +816,18 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) } } - return 0; + return SRT_STATUS_OK; } -int SrtCommon::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePre(SRTSOCKET sock) { - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; int no = 0; if (!m_tsbpdmode) { result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no); - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -835,7 +835,7 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) // This is for asynchronous connect. int maybe = m_blocking_mode; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &maybe, sizeof maybe); - if (result == -1) + if (result == SRT_ERROR) return result; // host is only checked for emptiness and depending on that the connection mode is selected. @@ -859,13 +859,13 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } void SrtCommon::SetupAdapter(const string& host, int port) { auto lsa = CreateAddr(host, port); - int stat = srt_bind(m_sock, lsa.get(), sizeof lsa); + SRTSTATUS stat = srt_bind(m_sock, lsa.get(), sizeof lsa); if (stat == SRT_ERROR) Error("srt_bind"); } @@ -885,10 +885,10 @@ void SrtCommon::OpenClient(string host, int port) void SrtCommon::PrepareClient() { m_sock = srt_create_socket(); - if (m_sock == SRT_ERROR) + if (m_sock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_sock); + SRTSTATUS stat = ConfigurePre(m_sock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -959,12 +959,12 @@ void SrtCommon::OpenGroupClient() } m_sock = srt_create_group(type); - if (m_sock == -1) + if (m_sock == SRT_ERROR) Error("srt_create_group"); srt_connect_callback(m_sock, &TransmitGroupSocketConnect, this); - int stat = -1; + int stat = SRT_ERROR; if (m_group_config != "") { Verb() << "Ignoring setting group config: '" << m_group_config; @@ -972,7 +972,7 @@ void SrtCommon::OpenGroupClient() stat = ConfigurePre(m_sock); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) Error("ConfigurePre"); if (!m_blocking_mode) @@ -1097,7 +1097,7 @@ void SrtCommon::OpenGroupClient() // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[i].id != SRT_INVALID_SOCK && targets[i].errorcode == SRT_SUCCESS) { m_group_nodes[i].socket = targets[i].id; } @@ -1107,14 +1107,14 @@ void SrtCommon::OpenGroupClient() // should be added to epoll. size_t size = m_group_data.size(); stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); stat = srt_group_data(m_sock, m_group_data.data(), &size); } - if (stat == -1) + if (stat == SRT_ERROR) { Error("srt_group_data"); } @@ -1123,7 +1123,7 @@ void SrtCommon::OpenGroupClient() for (size_t i = 0; i < m_group_nodes.size(); ++i) { SRTSOCKET insock = m_group_nodes[i].socket; - if (insock == -1) + if (insock == SRT_INVALID_SOCK) { Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; continue; @@ -1152,7 +1152,7 @@ void SrtCommon::OpenGroupClient() ready_conn, &len1, -1, // Wait infinitely NULL, NULL, - NULL, NULL) != -1) + NULL, NULL) != SRT_ERROR) { Verb() << "[C]" << VerbNoEOL; for (int i = 0; i < len1; ++i) @@ -1211,7 +1211,7 @@ void SrtCommon::OpenGroupClient() } stat = ConfigurePost(m_sock); - if (stat == -1) + if (stat == SRT_ERROR) { // This kind of error must reject the whole operation. // Usually you'll get this error on the first socket, @@ -1287,13 +1287,13 @@ void SrtCommon::ConnectClient(string host, int port) srt_connect_callback(m_sock, &TransmitConnectCallback, 0); } - int stat = -1; + SRTSTATUS stat = SRT_ERROR; for (;;) { ::transmit_throw_on_interrupt = true; - stat = srt_connect(m_sock, sa.get(), sizeof sa); + SRTSOCKET stats = srt_connect(m_sock, sa.get(), sizeof sa); ::transmit_throw_on_interrupt = false; - if (stat == SRT_ERROR) + if (stats == SRT_INVALID_SOCK) { int reason = srt_getrejectreason(m_sock); #if PLEASE_LOG @@ -1325,7 +1325,7 @@ void SrtCommon::ConnectClient(string host, int port) // Socket readiness for connection is checked by polling on WRITE allowed sockets. int lenc = 2, lene = 2; SRTSOCKET ready_connect[2], ready_error[2]; - if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != -1) + if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != SRT_ERROR) { // We should have just one socket, so check whatever socket // is in the transmit_error_storage. @@ -1502,7 +1502,7 @@ void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpda int result = d.result; SRT_MEMBERSTATUS mstatus = d.memberstate; - if (result != -1 && status == SRTS_CONNECTED) + if (result != SRT_ERROR && status == SRTS_CONNECTED) { // Short report with the state. Verb() << "G@" << id << "<" << MemberStatusStr(mstatus) << "> " << VerbNoEOL; @@ -1648,7 +1648,7 @@ bytevector SrtSource::GroupRead(size_t chunk) size_t size = m_group_data.size(); int stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); @@ -1660,7 +1660,7 @@ bytevector SrtSource::GroupRead(size_t chunk) m_group_data.resize(size); } - if (stat == -1) // Also after the above fix + if (stat == SRT_ERROR) // Also after the above fix { Error(UDT::getlasterror(), "FAILURE when reading group data"); } @@ -1696,7 +1696,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // Check first the ahead packets if you have any to deliver. - if (m_group_seqno != -1 && !m_group_positions.empty()) + if (m_group_seqno != SRT_SEQNO_NONE && !m_group_positions.empty()) { bytevector ahead_packet; @@ -1951,7 +1951,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // NOTE: checks against m_group_seqno and decisions based on it - // must NOT be done if m_group_seqno is -1, which means that we + // must NOT be done if m_group_seqno is NONE, which means that we // are about to deliver the very first packet and we take its // sequence number as a good deal. @@ -1961,11 +1961,11 @@ bytevector SrtSource::GroupRead(size_t chunk) // - check ordering. // The second one must be done always, but failed discrepancy // check should exclude the socket from any further checks. - // That's why the common check for m_group_seqno != -1 can't + // That's why the common check for m_group_seqno != NONE can't // embrace everything below. // We need to first qualify the sequence, just for a case - if (m_group_seqno != -1 && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) + if (m_group_seqno != SRT_SEQNO_NONE && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) { // This error should be returned if the link turns out // to be the only one, or set to the group data. @@ -1993,7 +1993,7 @@ bytevector SrtSource::GroupRead(size_t chunk) p->sequence = mctrl.pktseq; } - if (m_group_seqno != -1) + if (m_group_seqno != SRT_SEQNO_NONE) { // Now we can safely check it. int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_group_seqno); @@ -2082,7 +2082,7 @@ bytevector SrtSource::GroupRead(size_t chunk) // Update it now and don't do anything else with the sockets. // Sanity check - if (next_seq == -1) + if (next_seq == SRT_SEQNO_NONE) { Error("IPE: next_seq not set after output extracted!"); } @@ -2306,7 +2306,7 @@ MediaPacket SrtSource::Read(size_t chunk) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != SRT_ERROR) { Verb() << "... epoll reported ready " << len << " sockets"; // If the event was SRT_EPOLL_UPDATE, report it, and still wait. @@ -2409,7 +2409,7 @@ SrtTarget::SrtTarget(std::string host, int port, std::string path, const std::ma int SrtTarget::ConfigurePre(SRTSOCKET sock) { int result = SrtCommon::ConfigurePre(sock); - if (result == -1) + if (result == SRT_ERROR) return result; int yes = 1; @@ -2418,7 +2418,7 @@ int SrtTarget::ConfigurePre(SRTSOCKET sock) // In HSv4 this setting is obligatory; otherwise the SRT handshake // extension will not be done at all. result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; return 0; @@ -2437,7 +2437,7 @@ void SrtTarget::Write(const MediaPacket& data) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != SRT_ERROR) { bool any_write_ready = false; for (int i = 0; i < len; ++i) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..f7c49ddf6 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -142,7 +142,7 @@ class SrtCommon void Acquire(SRTSOCKET s) { m_sock = s; - if (s & SRTGROUP_MASK) + if (srt::isgroup(s)) m_listener_group = true; } @@ -153,8 +153,8 @@ class SrtCommon void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); void Init(string host, int port, string path, map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); - virtual int ConfigurePost(SRTSOCKET sock); - virtual int ConfigurePre(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePost(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePre(SRTSOCKET sock); void OpenClient(string host, int port); #if ENABLE_BONDING @@ -226,7 +226,7 @@ class SrtTarget: public virtual Target, public virtual SrtCommon SrtTarget(std::string host, int port, std::string path, const std::map& par); SrtTarget() {} - int ConfigurePre(SRTSOCKET sock) override; + SRTSTATUS ConfigurePre(SRTSOCKET sock) override; void Write(const MediaPacket& data) override; bool IsOpen() override { return IsUsable(); } bool Broken() override { return IsBroken(); } @@ -249,7 +249,7 @@ class SrtRelay: public Relay, public SrtSource, public SrtTarget SrtRelay(std::string host, int port, std::string path, const std::map& par); SrtRelay() {} - int ConfigurePre(SRTSOCKET sock) override + SRTSTATUS ConfigurePre(SRTSOCKET sock) override { // This overrides the change introduced in SrtTarget, // which sets the SRTO_SENDER flag. For a bidirectional transmission From 6ad10417a35b77b35a5b5d4f96fdefdb488f3372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Dec 2022 18:29:43 +0100 Subject: [PATCH 028/174] [MAINT] Fixed example buildbreaks. Added examples for message mode transmission --- .github/workflows/cxx11-ubuntu.yaml | 2 +- CMakeLists.txt | 4 + examples/recvlive.cpp | 10 -- examples/recvmsg.cpp | 215 ++++++++++++++++++++++++++++ examples/sendmsg.cpp | 210 +++++++++++++++++++++++++++ examples/test-c-client.c | 2 +- examples/test-c-server.c | 2 +- 7 files changed, 432 insertions(+), 13 deletions(-) create mode 100644 examples/recvmsg.cpp create mode 100644 examples/sendmsg.cpp diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 61a3f21d6..751b3fda1 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -16,7 +16,7 @@ jobs: - name: configure run: | mkdir _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON - name: build run: cd _build && cmake --build ./ - name: test diff --git a/CMakeLists.txt b/CMakeLists.txt index 7586e79e5..b62c67a95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1339,6 +1339,10 @@ if (ENABLE_EXAMPLES) srt_add_example(recvfile.cpp) + srt_add_example(sendmsg.cpp) + + srt_add_example(recvmsg.cpp) + srt_add_example(test-c-client.c) srt_add_example(example-client-nonblock.c) diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index b2a026d03..81c2b1ef7 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -53,16 +53,6 @@ int main(int argc, char* argv[]) return 0; } - // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, - // although there's also lots of other options to be set, for which there's a convenience option, - // SRTO_TRANSTYPE. - // SRT_TRANSTYPE tt = SRTT_LIVE; - // if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_TRANSTYPE, &tt, sizeof tt)) - // { - // cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; - // return 0; - // } - bool no = false; if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_RCVSYN, &no, sizeof no)) { diff --git a/examples/recvmsg.cpp b/examples/recvmsg.cpp new file mode 100644 index 000000000..008eb3928 --- /dev/null +++ b/examples/recvmsg.cpp @@ -0,0 +1,215 @@ +#ifndef WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +string CreateFilename(string fmt, int ord) +{ + ostringstream os; + + size_t pos = fmt.find('%'); + if (pos == string::npos) + os << fmt << ord << ".out"; + else + { + os << fmt.substr(0, pos) << ord << fmt.substr(pos+1); + } + return os.str(); +} + +int main(int argc, char* argv[]) +{ + string service("9000"); + if (argc > 1) + service = argv[1]; + + if (service == "--help") + { + cout << "usage: recvmsg [server_port] [filepattern]" << endl; + return 0; + } + + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) + { + cout << "illegal port number or port is busy.\n" << endl; + return 1; + } + + string outfileform; + if (argc > 2) + { + outfileform = argv[2]; + } + + // use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + SRTSOCKET sfd = srt_create_socket(); + if (SRT_INVALID_SOCK == sfd) + { + cout << "srt_socket: " << srt_getlasterror_str() << endl; + return 1; + } + + int file_mode = SRTT_FILE; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_TRANSTYPE, &file_mode, sizeof file_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + bool message_mode = true; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + if (SRT_ERROR == srt_bind(sfd, res->ai_addr, res->ai_addrlen)) + { + cout << "srt_bind: " << srt_getlasterror_str() << endl; + return 0; + } + + freeaddrinfo(res); + + cout << "server is ready at port: " << service << endl; + + if (SRT_ERROR == srt_listen(sfd, 10)) + { + cout << "srt_listen: " << srt_getlasterror_str() << endl; + return 1; + } + + char data[4096]; + + srt::sockaddr_any remote; + + int afd = srt_accept(sfd, remote.get(), &remote.len); + + if (afd == SRT_INVALID_SOCK) + { + cout << "srt_accept: " << srt_getlasterror_str() << endl; + return 1; + } + + cout << "Connection from " << remote.str() << " established\n"; + + bool save_to_files = true; + + if (outfileform != "") + save_to_files = true; + + int ordinal = 1; + + // the event loop + while (true) + { + SRT_SOCKSTATUS status = srt_getsockstate(afd); + if ((status == SRTS_BROKEN) || + (status == SRTS_NONEXIST) || + (status == SRTS_CLOSED)) + { + cout << "source disconnected. status=" << status << endl; + srt_close(afd); + break; + } + + int ret = srt_recvmsg(afd, data, sizeof(data)); + if (ret == SRT_ERROR) + { + cout << "srt_recvmsg: " << srt_getlasterror_str() << endl; + break; + } + if (ret == 0) + { + cout << "EOT\n"; + break; + } + + if (ret < 5) + { + cout << "WRONG MESSAGE SYNTAX\n"; + break; + } + + if (save_to_files) + { + string fname = CreateFilename(outfileform, ordinal++); + ofstream ofile(fname); + + if (!ofile.good()) + { + cout << "ERROR: can't create file: " << fname << " - skipping message\n"; + continue; + } + + ofile.write(data, ret); + ofile.close(); + cout << "Written " << ret << " bytes of message to " << fname << endl; + } + else + { + union + { + char chars[4]; + int32_t intval; + } first4; + + copy(data, data + 4, first4.chars); + + cout << "[" << ret << "B " << ntohl(first4.intval) << "] "; + for (int i = 4; i < ret; ++i) + cout << ShowChar(data[i]); + cout << endl; + } + } + + srt_close(afd); + srt_close(sfd); + + // use this function to release the UDT library + srt_cleanup(); + + return 0; +} + +// Local Variables: +// c-file-style: "ellemtel" +// c-basic-offset: 3 +// compile-command: "g++ -Wall -O2 -std=c++11 -I.. -I../srtcore -o recvlive recvlive.cpp -L.. -lsrt -lpthread -L/usr/local/opt/openssl/lib -lssl -lcrypto" +// End: diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp new file mode 100644 index 000000000..53a0f66a5 --- /dev/null +++ b/examples/sendmsg.cpp @@ -0,0 +1,210 @@ +#ifndef _WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +int main(int argc, char* argv[]) +{ + if ((argc != 4) || (0 == atoi(argv[2]))) + { + cout << "usage: sendmsg server_ip server_port source_filename" << endl; + return -1; + } + + // Use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + struct addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + SRTSOCKET fhandle = srt_create_socket(); + // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, + // although there's also lots of other options to be set, for which there's a convenience option, + // SRTO_TRANSTYPE. + SRT_TRANSTYPE tt = SRTT_FILE; + srt_setsockopt(fhandle, 0, SRTO_TRANSTYPE, &tt, sizeof tt); + + bool message_mode = true; + srt_setsockopt(fhandle, 0, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode); + + if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) + { + cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; + return -1; + } + + // Connect to the server, implicit bind. + if (SRT_ERROR == srt_connect(fhandle, peer->ai_addr, peer->ai_addrlen)) + { + int rej = srt_getrejectreason(fhandle); + cout << "connect: " << srt_getlasterror_str() << ":" << srt_rejectreason_str(rej) << endl; + return -1; + } + + freeaddrinfo(peer); + + string source_fname = argv[3]; + + bool use_filelist = false; + if (source_fname[0] == '+') + { + use_filelist = true; + source_fname = source_fname.substr(1); + } + + istream* pin; + ifstream fin; + if (source_fname == "-") + { + pin = &cin; + } + else + { + fin.open(source_fname.c_str()); + pin = &fin; + } + + // The syntax is: + // + // 1. If the first character is +, it's followed by TTL in milliseconds. + // 2. Otherwise the first number is the ID, followed by a space, to be filled in first 4 bytes. + // 3. Rest of the characters, up to the end of line, should be put into a solid block and sent at once. + + int status = 0; + + int ordinal = 1; + int lpos = 0; + + for (;;) + { + string line; + int32_t id = 0; + int ttl = -1; + + getline(*pin, (line)); + if (pin->eof()) + break; + + // Interpret + if (line.size() < 2) + continue; + + if (use_filelist) + { + // The line should contain [+TTL] FILENAME + char fname[1024] = ""; + if (line[0] == '+') + { + int nparsed = sscanf(line.c_str(), "+%d %n%1000s", &ttl, &lpos, fname); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + line = fname; + } + + ifstream ifile (line); + + id = ordinal; + ++ordinal; + + if (!ifile.good()) + { + cout << "ERROR: file '" << line << "' cannot be read, skipping\n"; + continue; + } + + line = string(istreambuf_iterator(ifile), istreambuf_iterator()); + } + else + { + int lpos = 0; + + int nparsed = 0; + if (line[0] == '+') + { + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + else + { + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + if (nparsed != 1) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + } + + union + { + char chars[4]; + int32_t intval; + } first4; + first4.intval = htonl(id); + + vector input; + copy(first4.chars, first4.chars+4, back_inserter(input)); + copy(line.begin() + lpos, line.end(), back_inserter(input)); + + // CHECK CODE + // cout << "WILL SEND: TTL=" << ttl << " "; + // transform(input.begin(), input.end(), + // ostream_iterator(cout), ShowChar); + // cout << endl; + + int nsnd = srt_sendmsg(fhandle, &input[0], input.size(), ttl, false); + if (nsnd == SRT_ERROR) + { + cout << "SRT ERROR: " << srt_getlasterror_str() << endl; + status = SRT_ERROR; + break; + } + } + + srt_close(fhandle); + + // Signal to the SRT library to clean up all allocated sockets and resources. + srt_cleanup(); + + return status; +} diff --git a/examples/test-c-client.c b/examples/test-c-client.c index a4d99745c..71f6d30e4 100644 --- a/examples/test-c-client.c +++ b/examples/test-c-client.c @@ -59,7 +59,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/examples/test-c-server.c b/examples/test-c-server.c index f81f297eb..f7faf6fba 100644 --- a/examples/test-c-server.c +++ b/examples/test-c-server.c @@ -54,7 +54,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; From 29e8c1944fb02c2f7ef5a95684132a26613ab2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 11:25:40 +0100 Subject: [PATCH 029/174] Fixed wrong assertion for drop snd buf sequence check --- srtcore/buffer_snd.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 7e55ecad4..2f29a2c6c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -494,7 +494,13 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); - SRT_ASSERT(w_drop.seqno[Drop::END] == p->m_iSeqNo); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[Drop::END] == CSeqNo::decseq(p->m_iSeqNo)); return READ_DROP; } From ce98b8ac53d69cd2b7df8e5f8b8724c1df988594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 12:50:42 +0100 Subject: [PATCH 030/174] [MAINT] Refax for CSndBuffer::readData to make the call form clearer --- srtcore/buffer_snd.cpp | 50 +++++++++++++++++++++++++++--------------- srtcore/buffer_snd.h | 25 ++++++++++++++++++--- srtcore/core.cpp | 25 ++++++++++----------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index e945b166c..d63f97519 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -421,9 +421,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, Drop& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -438,19 +439,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -462,11 +467,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -476,18 +481,27 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[Drop::BEGIN] = w_packet.m_iSeqNo; + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[Drop::END] == CSeqNo::decseq(p->m_iSeqNo)); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5dce96ae0..d0dcdd453 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct Drop + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. + /// @param [in,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). + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, Drop& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d6b4d7389..1822e8213 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9071,28 +9071,25 @@ int srt::CUDT::packLostData(CPacket& w_packet) } } - int msglen; + CSndBuffer::Drop buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[CSndBuffer::Drop::BEGIN], buffer_drop.seqno[CSndBuffer::Drop::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[CSndBuffer::Drop::BEGIN] << " - " + << buffer_drop.seqno[CSndBuffer::Drop::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[CSndBuffer::Drop::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[CSndBuffer::Drop::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored From 333eaf2eab8979c6321b9940a19cbef159898f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 13:35:40 +0100 Subject: [PATCH 031/174] [MAINT] Removed reference fields from CPacket pinned to the SRT header --- srtcore/buffer_snd.cpp | 14 +-- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 223 ++++++++++++++++++----------------- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +-- test/test_fec_rebuilding.cpp | 6 +- 11 files changed, 155 insertions(+), 143 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index d63f97519..2f29a2c6c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -320,7 +320,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -346,7 +346,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -448,7 +448,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // This is rexmit request, so the packet should have the sequence number // already set when it was once sent uniquely. - SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -492,8 +492,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // to simply take the sequence number from the block. But this is a new // feature and should be only used after refax for the sender buffer to // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. - w_drop.seqno[Drop::BEGIN] = w_packet.m_iSeqNo; - w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); // Note the rules: here `p` is pointing to the first block AFTER the // message to be dropped, so the end sequence should be one behind @@ -514,7 +514,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -522,7 +522,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 30fd98778..4f752f290 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -613,8 +613,8 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) 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 - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1822e8213..ef3734518 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1963,7 +1963,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2494,7 +2494,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2521,7 +2521,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3525,7 +3525,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(0); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3540,7 +3540,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3605,7 +3605,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3823,17 +3823,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? 0 : m_ConnRes.m_iID); bool status = true; @@ -3844,7 +3844,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3876,8 +3876,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3889,7 +3889,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3899,16 +3899,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request); + m_pSndQueue->sendto(serv_addr, reqpkt); return status; } @@ -5679,15 +5679,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5698,10 +5698,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show()); } #endif @@ -5711,7 +5711,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } // This function is required to be called when a caller receives an INDUCTION @@ -5917,7 +5917,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -7625,8 +7625,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + ctrlpkt.set_id(m_PeerID); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7640,7 +7640,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7661,7 +7661,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7691,7 +7691,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); m_tsLastWarningTime = steady_clock::now(); @@ -7700,14 +7700,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7716,21 +7716,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7817,7 +7817,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8024,7 +8024,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8716,18 +8716,18 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) + (rsppkt), (initdata))) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -8849,7 +8849,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9024,37 +9024,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9064,7 +9071,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; @@ -9114,7 +9121,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9215,7 +9222,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9227,14 +9234,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9243,7 +9250,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9340,14 +9347,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9357,7 +9364,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9376,7 +9383,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9462,7 +9469,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9476,7 +9483,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9484,7 +9491,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9493,7 +9500,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9504,17 +9511,17 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // packet are not present in the buffer (preadte the send buffer). // Override extraction sequence with scheduling sequence. - m_iSndCurrSeqNo = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); ScopedLock ackguard(m_RecvAckLock); - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << m_iSndCurrSeqNo << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9526,15 +9533,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = m_iSndCurrSeqNo; + w_packet.set_seqno(m_iSndCurrSeqNo); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9554,7 +9561,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -9725,7 +9732,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -9750,7 +9757,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -9763,7 +9770,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -9785,7 +9792,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -9796,7 +9803,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -9887,7 +9894,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -9901,12 +9908,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -9914,9 +9921,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -9964,7 +9971,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10008,7 +10015,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10034,7 +10041,7 @@ int srt::CUDT::processData(CUnit* in_unit) // supply the missing packet(s), and the loss will no longer be visible for the code that follows. if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing { - const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10050,13 +10057,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10122,7 +10129,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10407,7 +10414,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10427,7 +10434,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10642,7 +10649,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: - // [[using assert(packet.m_iID == 0)]] + // [[using assert(packet.id() == 0)]] HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); @@ -10723,7 +10730,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -10791,7 +10798,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -10839,8 +10846,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } @@ -10951,7 +10958,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp); } else @@ -10962,7 +10969,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index bf5db8c0d..a855552cd 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -600,7 +596,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index e6d2516a9..4f17d4df4 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -349,12 +349,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1b05c4f4e..e7a9ca2bb 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 1bdff1848..bb6920310 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -894,7 +894,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : 0; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -1380,7 +1380,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 31e05b205..a9f3bdba1 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1089,6 +1089,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index ecc4a4947..a9dba46ba 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -236,7 +236,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -257,7 +257,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -267,7 +267,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -282,7 +282,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 15356c8f2..db0db317a 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,21 +54,26 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + } + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..91d269221 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -792,7 +792,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -869,7 +869,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -887,7 +887,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); From 124ec2b6f54534758e688c10ddc032107a1c9997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 13:21:02 +0100 Subject: [PATCH 032/174] [BUG] Fixed default reject reason for a listener callback --- srtcore/core.cpp | 4 ++++ test/test_listen_callback.cpp | 29 ++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 92c53e988..a065fa3c3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -62,6 +62,7 @@ modified by #include #include #include "srt.h" +#include "access_control.h" // Required for SRT_REJX_FALLBACK #include "queue.h" #include "api.h" #include "core.h" @@ -11592,6 +11593,8 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak acore->m_HSGroupType = gt; #endif + // Set the default value + acore->m_RejectReason = SRT_REJX_FALLBACK; try { int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs.m_iVersion, peer, target); @@ -11604,6 +11607,7 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak return false; } + acore->m_RejectReason = SRT_REJ_UNKNOWN; return true; } diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index 4e08715de..a3bdb3003 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -9,6 +9,7 @@ #endif #include "srt.h" +#include "access_control.h" #include "utilities.h" srt_listen_callback_fn SrtTestListenCallback; @@ -98,8 +99,8 @@ class ListenerCallback { if (results[0].events == SRT_EPOLL_IN) { - int acp = srt_accept(server_sock, NULL, NULL); - if (acp == SRT_ERROR) + SRTSOCKET acp = srt_accept(server_sock, NULL, NULL); + if (acp == SRT_INVALID_SOCK) { std::cout << "[T] Accept failed, so exitting\n"; break; @@ -140,7 +141,7 @@ class ListenerCallback }; -int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion, const struct sockaddr* peeraddr, const char* streamid) +int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid) { using namespace std; @@ -192,6 +193,7 @@ int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion if (!found) { + srt_setrejectreason(ns, SRT_REJX_UNAUTHORIZED); cerr << "TEST: USER NOT FOUND, returning false.\n"; return -1; } @@ -246,6 +248,8 @@ TEST_F(ListenerCallback, SecureSuccess) // EXPECTED RESULT: connected successfully EXPECT_NE(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJ_UNKNOWN); } #if SRT_ENABLE_ENCRYPTION @@ -259,6 +263,8 @@ TEST_F(ListenerCallback, FauxPass) // EXPECTED RESULT: connection rejected EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJ_BADSECRET); } #endif @@ -274,7 +280,24 @@ TEST_F(ListenerCallback, FauxUser) // EXPECTED RESULT: connection rejected EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJX_FALLBACK); } +TEST_F(ListenerCallback, FauxSyntax) +{ + string username_spec = "#!::r=mystream,t=publish"; // No 'u' key specified + string password = "thelocalmanager"; + + ASSERT_NE(srt_setsockflag(client_sock, SRTO_STREAMID, username_spec.c_str(), username_spec.size()), -1); +#if SRT_ENABLE_ENCRYPTION + ASSERT_NE(srt_setsockflag(client_sock, SRTO_PASSPHRASE, password.c_str(), password.size()), -1); +#endif + + // EXPECTED RESULT: connection rejected + EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJX_UNAUTHORIZED); +} From f14c349cbf2ec8cc980504b7a7c467820650a232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 20:54:58 +0100 Subject: [PATCH 033/174] Added extended reject reason string API --- srtcore/access_control.h | 2 +- srtcore/core.cpp | 6 +++- srtcore/srt.h | 1 + srtcore/srt_c_api.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ testing/testmedia.cpp | 21 +++++++++++--- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/srtcore/access_control.h b/srtcore/access_control.h index 611e1dad8..18357aeee 100644 --- a/srtcore/access_control.h +++ b/srtcore/access_control.h @@ -66,7 +66,7 @@ written by #define SRT_REJX_GW 1502 // The server acts as a gateway and the target endpoint rejected the connection. #define SRT_REJX_DOWN 1503 // The service has been temporarily taken over by a stub reporting this error. The real service can be down for maintenance or crashed. // CODE NOT IN USE 504: unused: timeout not supported -#define SRT_REJX_VERSION 1505 // SRT version not supported. This might be either unsupported backward compatibility, or an upper value of a version. +#define SRT_REJX_VERSION 1505 // Version not supported - either SRT or the application itself. This might be either unsupported backward compatibility, or an upper value of a version. // CODE NOT IN USE 506: unused: negotiation and references not supported #define SRT_REJX_NOROOM 1507 // The data stream cannot be archived due to lacking storage space. This is in case when the request type was to send a file or the live stream to be archived. // CODE NOT IN USE 508: unused: no redirection supported diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a065fa3c3..ed6e3c9c0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4441,9 +4441,12 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx } HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show()); - if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) + if (m_ConnRes.m_iReqType >= URQ_FAILURE_TYPES) { m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); + LOGC(cnlog.Warn, + log << CONID() << "processConnectResponse: rejecting per reception of a rejection HS response: " + << RequestTypeStr(m_ConnRes.m_iReqType)); return CONN_REJECT; } @@ -4626,6 +4629,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // in rendezvous it's completed before calling this function. if (!rendezvous) { + HLOGC(cnlog.Debug, log << CONID() << boolalpha << "postConnect: packet:" << bool(pResponse) << " rendezvous:" << rendezvous); // The "local storage depleted" case shouldn't happen here, but // this is a theoretical path that needs prevention. bool ok = pResponse; diff --git a/srtcore/srt.h b/srtcore/srt.h index d5a5eb196..9d7fb966c 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -922,6 +922,7 @@ SRT_API int srt_setrejectreason(SRTSOCKET sock, int value); // Planned removal: v1.6.0. SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg []; SRT_API const char* srt_rejectreason_str(int id); +SRT_API const char* srt_rejectreasonx_str(int id); SRT_API uint32_t srt_getversion(void); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index b3b67d327..827a3848a 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -17,7 +17,10 @@ written by #include #include +#include +#include #include "srt.h" +#include "access_control.h" #include "common.h" #include "packet.h" #include "core.h" @@ -470,6 +473,11 @@ extern const char* const srt_rejectreason_msg[] = { const char* srt_rejectreason_str(int id) { + if (id == SRT_REJX_FALLBACK) + { + return "Application fallback (default) rejection reason"; + } + if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -481,4 +489,58 @@ const char* srt_rejectreason_str(int id) return srt_rejection_reason_msg[id]; } +// NOTE: values in the first field must be sorted by numbers. +pair srt_rejectionx_reason_msg [] = { + + // Internal + make_pair(SRT_REJX_FALLBACK, "Default fallback reason"), + make_pair(SRT_REJX_KEY_NOTSUP, "Unsupported streamid key"), + make_pair(SRT_REJX_FILEPATH, "Incorrect resource path"), + make_pair(SRT_REJX_HOSTNOTFOUND, "Unrecognized host under h key"), + + // HTTP adopted codes + make_pair(SRT_REJX_BAD_REQUEST, "Bad request"), + make_pair(SRT_REJX_UNAUTHORIZED, "Unauthorized"), + make_pair(SRT_REJX_OVERLOAD, "Server overloaded or underpaid"), + make_pair(SRT_REJX_FORBIDDEN, "Resource access forbidden"), + make_pair(SRT_REJX_BAD_MODE, "Bad mode specified with m key"), + make_pair(SRT_REJX_UNACCEPTABLE, "Unacceptable parameters for specified resource"), + make_pair(SRT_REJX_CONFLICT, "Access conflict for a locked resource"), + make_pair(SRT_REJX_NOTSUP_MEDIA, "Unsupported media type specified with t key"), + make_pair(SRT_REJX_LOCKED, "Resource locked for any access"), + make_pair(SRT_REJX_FAILED_DEPEND, "Dependent session id expired"), + make_pair(SRT_REJX_ISE, "Internal server error"), + make_pair(SRT_REJX_GW, "Gateway target rejected connection"), + make_pair(SRT_REJX_DOWN, "Service is down for maintenance"), + make_pair(SRT_REJX_VERSION, "Unsupported version for the request"), + make_pair(SRT_REJX_NOROOM, "Storage capacity exceeded"), +}; + +struct FCompareItems +{ + bool operator()(const pair& a, int b) + { + return a.first < b; + } +}; + +const char* srt_rejectreasonx_str(int id) +{ + if (id < SRT_REJX_FALLBACK) + { + return "System-defined rejection reason (not extended)"; + } + + pair* begin = srt_rejectionx_reason_msg; + pair* end = begin + Size(srt_rejectionx_reason_msg); + pair* found = lower_bound(begin, end, id, FCompareItems()); + + if (found == end || found->first != id) + { + return "Undefined extended rejection code"; + } + + return found->second; +} + } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b07f92ae8..acb63109a 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -99,6 +99,19 @@ string DirectionName(SRT_EPOLL_T direction) return dir_name; } +static string RejectReasonStr(int id) +{ + if (id < SRT_REJC_PREDEFINED) + return srt_rejectreason_str(id); + + if (id < SRT_REJC_USERDEFINED) + return srt_rejectreasonx_str(id); + + ostringstream sout; + sout << "User-defined reason code " << id; + return sout.str(); +} + template inline bytevector FileRead(FileBase& ifile, size_t chunk, const string& filename) { @@ -1054,7 +1067,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1178,7 +1191,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1387,11 +1400,11 @@ void SrtCommon::Error(string src, int reason, int force_result) if ( Verbose::on ) Verb() << "FAILURE\n" << src << ": [" << result << "] " << "Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); else cerr << "\nERROR #" << result << ": Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); } else { From 09d480d55b5de6a31549cb4f1658891538662455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 21:01:40 +0100 Subject: [PATCH 034/174] Fixed testing app to set unauthorized rejection code for a better example --- testing/srt-test-live.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 7bf6bb51c..ed8036e11 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -71,11 +71,11 @@ #include "testmedia.hpp" // requires access to SRT-dependent globals #include "verbose.hpp" -// NOTE: This is without "haisrt/" because it uses an internal path +// NOTE: This is without "srt/" because it uses an internal path // to the library. Application using the "installed" library should // use #include -#include // This TEMPORARILY contains extra C++-only SRT API. +#include #include using namespace std; @@ -354,6 +354,7 @@ extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const soc // This hook sets the password to the just accepted socket // depending on the user + srt_setrejectreason(acpsock, SRT_REJX_UNAUTHORIZED); string exp_pw = passwd.at(username); srt_setsockflag(acpsock, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()); From 08d1e5cfcf27b0dc5376ef5a4507854a5293a552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Jan 2023 09:26:31 +0100 Subject: [PATCH 035/174] Minor fixes --- srtcore/api.cpp | 2 +- srtcore/buffer_rcv.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e18407238..84599efb2 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -229,7 +229,7 @@ srt::CUDTUnited::~CUDTUnited() string srt::CUDTUnited::CONID(SRTSOCKET sock) { - if (int32_t(sock) <= 0) // both SRT_INVALID_SOCK and SRT_SOCKID_CONNREQ and illegal negative domain + if (int32_t(sock) <= 0) // embraces SRT_INVALID_SOCK, SRT_SOCKID_CONNREQ and illegal negative domain return ""; std::ostringstream os; diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 373d7da11..4d06684fd 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -262,10 +262,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { - if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. - { - HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with mflag field set to a negative value!"); - } IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; int iDropCnt = 0; @@ -303,6 +299,10 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } return iDropCnt; } + if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. + { + HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with msgflag field set to a negative value!"); + } // Drop by packet seqno range. const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); From b53a091777719f9594efba9d6f0ed1582839f1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 1 Mar 2023 16:41:36 +0100 Subject: [PATCH 036/174] [FIX] Calculate correctly max payload size per UDP packet --- apps/srt-file-transmit.cpp | 7 +- apps/transmitmedia.cpp | 37 ++++++++++- srtcore/api.cpp | 17 +++-- srtcore/buffer_rcv.cpp | 9 ++- srtcore/buffer_snd.cpp | 3 +- srtcore/buffer_snd.h | 2 +- srtcore/buffer_tools.cpp | 5 +- srtcore/buffer_tools.h | 3 +- srtcore/channel.cpp | 9 +++ srtcore/common.cpp | 2 +- srtcore/common.h | 9 +++ srtcore/congctl.cpp | 5 +- srtcore/core.cpp | 97 +++++++++++++++++++++------ srtcore/core.h | 30 ++++++--- srtcore/group.cpp | 15 +++-- srtcore/group_backup.h | 1 + srtcore/packet.h | 18 +++-- srtcore/packetfilter_api.h | 2 +- srtcore/socketconfig.cpp | 89 +++++++++++++++---------- srtcore/socketconfig.h | 2 + srtcore/srt.h | 7 +- srtcore/stats.h | 83 ++++++++++++++++++----- srtcore/window.cpp | 8 +-- srtcore/window.h | 31 +++++++-- test/test_fec_rebuilding.cpp | 8 +-- test/test_file_transmission.cpp | 113 +++++++++++++++++++++++++++++++- testing/srt-test-mpbond.cpp | 2 +- testing/testmedia.cpp | 45 ++++++++++--- 28 files changed, 523 insertions(+), 136 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..e29ae3248 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -180,7 +180,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) return 2; } - cfg.chunk_size = stoul(Option(params, "1456", o_chunk)); + cfg.chunk_size = stoul(Option(params, "0", o_chunk)); cfg.skip_flushing = Option(params, false, o_no_flush); cfg.bw_report = stoi(Option(params, "0", o_bwreport)); cfg.stats_report = stoi(Option(params, "0", o_statsrep)); @@ -681,8 +681,11 @@ int main(int argc, char** argv) // // Set global config variables // - if (cfg.chunk_size != SRT_LIVE_MAX_PLSIZE) + if (cfg.chunk_size != 0) transmit_chunk_size = cfg.chunk_size; + else + transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET; + transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf); transmit_bw_report = cfg.bw_report; transmit_stats_report = cfg.stats_report; diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 260323ef3..42970d92f 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -44,7 +44,7 @@ bool g_stats_are_printed_to_stdout = false; bool transmit_total_stats = false; unsigned long transmit_bw_report = 0; unsigned long transmit_stats_report = 0; -unsigned long transmit_chunk_size = SRT_LIVE_MAX_PLSIZE; +unsigned long transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET6; class FileSource: public Source { @@ -179,6 +179,36 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } + int fam_to_limit_size = AF_INET6; // take the less one as default + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + fam_to_limit_size = host_sa.family(); + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + } + + if (adapter != "" && adapter != host) + { + adapter_sa = CreateAddr(adapter); + fam_to_limit_size = adapter_sa.family(); + + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + } + + if (fam_to_limit_size != AF_INET) + fam_to_limit_size = AF_INET6; + if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { m_tsbpdmode = false; @@ -195,8 +225,9 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); + if (transmit_chunk_size > size_limit) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 63dab3927..4a22cb8ec 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -618,7 +618,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // bind to the same addr of listening socket - ns->core().open(); + ns->core().open(peer.family()); if (!updateListenerMux(ns, ls)) { // This is highly unlikely if not impossible, but there's @@ -928,7 +928,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - s->core().open(); + s->core().open(name.family()); updateMux(s, name); s->m_Status = SRTS_OPENED; @@ -957,7 +957,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->core().open(); + s->core().open(name.family()); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; @@ -1847,7 +1847,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->core().open(); + s->core().open(target_addr.family()); sockaddr_any autoselect_sa(target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). @@ -3157,7 +3157,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; - m.m_pRcvQueue->init(128, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + // We can't use maxPayloadSize() because this value isn't valid until the connection is established. + // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. + size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + + HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 + << " plsize=" << payload_size << " hsize=" << 1024); + m.m_pRcvQueue->init(128, payload_size, 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. diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 3098407a6..a2a3d3d65 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -130,7 +130,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) - , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) + , m_uAvgPayloadSz(0) { SRT_ASSERT(size < size_t(std::numeric_limits::max())); // All position pointers are integers } @@ -751,7 +751,12 @@ void CRcvBuffer::countBytes(int pkts, int bytes) 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); + { + if (!m_uAvgPayloadSz) + m_uAvgPayloadSz = bytes; + else + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + } } void CRcvBuffer::releaseUnitInPos(int pos) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 26f885dd6..9f224a678 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -64,7 +64,7 @@ using namespace std; using namespace srt_logging; using namespace sync; -CSndBuffer::CSndBuffer(int size, int maxpld, int authtag) +CSndBuffer::CSndBuffer(int ip_family, int size, int maxpld, int authtag) : m_BufLock() , m_pBlock(NULL) , m_pFirstBlock(NULL) @@ -77,6 +77,7 @@ CSndBuffer::CSndBuffer(int size, int maxpld, int authtag) , m_iAuthTagSize(authtag) , m_iCount(0) , m_iBytesCount(0) + , m_rateEstimator(ip_family) { // initial physical buffer of "size" m_pBuffer = new Buffer; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 4440b9bfd..5da8c9631 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -87,7 +87,7 @@ class CSndBuffer /// @param size initial number of blocks (each block to store one packet payload). /// @param maxpld maximum packet payload (including auth tag). /// @param authtag auth tag length in bytes (16 for GCM, 0 otherwise). - CSndBuffer(int size = 32, int maxpld = 1500, int authtag = 0); + CSndBuffer(int ip_family, int size, int maxpld, int authtag); ~CSndBuffer(); public: diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 1cdc72d58..e0ecb81fb 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -102,11 +102,12 @@ void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); } -CRateEstimator::CRateEstimator() +CRateEstimator::CRateEstimator(int family) : m_iInRatePktsCount(0) , m_iInRateBytesCount(0) , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) + , m_iFullHeaderSize(CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE) {} void CRateEstimator::setInputRateSmpPeriod(int period) @@ -141,7 +142,7 @@ void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes 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 * m_iFullHeaderSize); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index a6d81a356..828690b95 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -93,7 +93,7 @@ class CRateEstimator typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; public: - CRateEstimator(); + CRateEstimator(int family); public: uint64_t getInRatePeriod() const { return m_InRatePeriod; } @@ -125,6 +125,7 @@ class CRateEstimator time_point m_tsInRateStartTime; uint64_t m_InRatePeriod; // usec int m_iInRateBps; // Input Rate in Bytes/sec + int m_iFullHeaderSize; }; } diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 31a29092f..ec3c68beb 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1005,6 +1005,15 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet if ((msg_flags & errmsgflg[i].first) != 0) flg << " " << errmsgflg[i].second; + if (msg_flags & MSG_TRUNC) + { + // Additionally show buffer information in this case + flg << " buffers: "; + for (size_t i = 0; i < CPacket::PV_SIZE; ++i) + { + flg << "[" << w_packet.m_PacketVector[i].iov_len << "] "; + } + } // This doesn't work the same way on Windows, so on Windows just skip it. #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 53148136a..a39312ec3 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -209,7 +209,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc { // Check if the peer address is a model of IPv4-mapped-on-IPv6. // If so, it means that the `ip` array should be interpreted as IPv4. - const bool is_mapped_ipv4 = checkMappedIPv4((uint16_t*)peer.sin6.sin6_addr.s6_addr); + const bool is_mapped_ipv4 = checkMappedIPv4(peer.sin6); sockaddr_in6* a = (&w_addr.sin6); diff --git a/srtcore/common.h b/srtcore/common.h index 5f3dd7f18..7dc84ea14 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1420,6 +1420,15 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(const std::string& s, SrtConfig& w_config); +bool checkMappedIPv4(const uint16_t* sa); + +inline bool checkMappedIPv4(const sockaddr_in6& sa) +{ + const uint16_t* addr = reinterpret_cast(&sa.sin6_addr.s6_addr); + return checkMappedIPv4(addr); +} + + } // namespace srt #endif diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 91c73d660..b9265c046 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -63,6 +63,7 @@ class LiveCC: public SrtCongestionControlBase int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; + size_t m_zHeaderSize; // NAKREPORT stuff. int m_iMinNakInterval_us; // Minimum NAK Report Period (usec) @@ -81,6 +82,8 @@ class LiveCC: public SrtCongestionControlBase m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; + m_zHeaderSize = parent->m_config.iMSS - parent->maxPayloadSize(); + m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) @@ -173,7 +176,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; 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 e6da157a5..b4d6160cf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -269,6 +269,7 @@ void srt::CUDT::construct() m_pSndQueue = NULL; m_pRcvQueue = NULL; + m_TransferIPVersion = AF_UNSPEC; // Will be set after connection m_pSNode = NULL; m_pRNode = NULL; @@ -305,7 +306,9 @@ void srt::CUDT::construct() // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } -srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent) + : m_parent(parent) + , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -328,7 +331,9 @@ srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) } -srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor) + : m_parent(parent) + , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -468,13 +473,17 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int); break; + // For SNDBUF/RCVBUF values take the variant that uses more memory. + // It is not possible to make sure what "family" is in use without + // checking if the socket is bound. This will also be the exact size + // of the memory in use. case SRTO_SNDBUF: - *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); optlen = sizeof(int); break; @@ -756,7 +765,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_PAYLOADSIZE: optlen = sizeof(int); - *(int *)optval = (int) m_config.zExpPayloadSize; + *(int *)optval = (int) payloadSize(); break; case SRTO_KMREFRESHRATE: @@ -872,11 +881,15 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. // Initial sequence number, loss, acknowledgement, etc. -void srt::CUDT::clearData() +void srt::CUDT::clearData(int family) { - m_iMaxSRTPayloadSize = m_config.iMSS - CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_SndTimeWindow.initialize(full_hdr_size, m_iMaxSRTPayloadSize); + m_RcvTimeWindow.initialize(full_hdr_size, m_iMaxSRTPayloadSize); + m_iEXPCount = 1; m_iBandwidth = 1; // pkts/sec // XXX use some constant for this 16 @@ -919,11 +932,11 @@ void srt::CUDT::clearData() m_tsRcvPeerStartTime = steady_clock::time_point(); } -void srt::CUDT::open() +void srt::CUDT::open(int family) { ScopedLock cg(m_ConnectionLock); - clearData(); + clearData(family); // structures for queue if (m_pSNode == NULL) @@ -3025,7 +3038,9 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) m_config.sPacketFilterConfig.set(confstr); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; + // XXX Using less maximum payload size of IPv4 and IPv6; this is only about the payload size + // for live. + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET6 - cfg.extra_size; if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, @@ -4659,10 +4674,19 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } + m_TransferIPVersion = m_PeerAddr.family(); + if (m_PeerAddr.family() == AF_INET6) + { + // Check if the m_PeerAddr's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(m_PeerAddr.sin6)) + m_TransferIPVersion = AF_INET; + } + // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; + const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -5508,7 +5532,7 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } return iDropCnt; @@ -5532,7 +5556,7 @@ void srt::CUDT::setInitialRcvSeq(int32_t isn) const int iDropCnt = m_pRcvBuffer->dropAll(); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); sync::ScopedLock sl(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } m_pRcvBuffer->setStartSeqNo(isn); @@ -5572,8 +5596,30 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd try { + // XXX SND buffer may allocate more memory, but must set the size of a single + // packet that fits the transmission for the overall connection. For any mixed 4-6 + // connection it should be the less size, that is, for IPv6 + const int authtag = m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM ? HAICRYPT_AUTHTAG_MAX : 0; - m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize, authtag); + + SRT_ASSERT(m_iMaxSRTPayloadSize != 0); + SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); + // IMPORTANT: + // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent + // over the current connection - which means that if any party is IPv6, then the maximum size + // is the one for IPv6 (1444). Only if both parties are IPv4, this maximum size is 1456. + // The family as the first argument is something different - it's for the header size in order + // to calculate rate and statistics. + + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); + + HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size + << " snd-bufsize=" << 32 << " TF-IPv" + << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") + << " authtag=" << authtag); + + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. @@ -5651,8 +5697,17 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& rewriteHandshakeData(peer, (w_hs)); - int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; - m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; + m_TransferIPVersion = peer.family(); + if (peer.family() == AF_INET6) + { + // Check if the peer's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(peer.sin6)) + m_TransferIPVersion = AF_INET; + } + + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); // Prepare all structures @@ -7285,7 +7340,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(m_TransferIPVersion == AF_UNSPEC ? AF_INET : m_TransferIPVersion); { ScopedLock statsguard(m_StatsLock); @@ -8861,7 +8916,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } } @@ -9956,8 +10011,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10158,7 +10213,7 @@ int srt::CUDT::processData(CUnit* in_unit) ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); + m_stats.rcvr.lost.count(stats::BytesPacketsCount(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " diff --git a/srtcore/core.h b/srtcore/core.h index ce0e53958..67ee0dcb7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -179,7 +179,7 @@ class CUDT private: // constructor and desctructor void construct(); - void clearData(); + void clearData(int family); CUDT(CUDTSocket* parent); CUDT(CUDTSocket* parent, const CUDT& ancestor); const CUDT& operator=(const CUDT&) {return *this;} // = delete ? @@ -328,6 +328,19 @@ class CUDT int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout_ms; } size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } + size_t payloadSize() const + { + // If payloadsize is set, it should already be checked that + // it is less than the possible maximum payload size. So return it + // if it is set to nonzero value. In case when the connection isn't + // yet established, return also 0, if the value wasn't set. + if (m_config.zExpPayloadSize || !m_bConnected) + return m_config.zExpPayloadSize; + + // If SRTO_PAYLOADSIZE was remaining with 0 (default for FILE mode) + // then return the maximum payload size per packet. + return m_iMaxSRTPayloadSize; + } int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -446,7 +459,7 @@ class CUDT private: /// initialize a UDT entity and bind to a local address. - void open(); + void open(int family); /// Start listening to any connection request. void setListenState(); @@ -728,13 +741,6 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - CRateEstimator getRateEstimator() const - { - if (!m_pSndBuffer) - return CRateEstimator(); - return m_pSndBuffer->getRateEstimator(); - } - void setRateEstimator(const CRateEstimator& rate) { if (!m_pSndBuffer) @@ -1134,6 +1140,11 @@ class CUDT int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration + + CoreStats(int* payloadsize_loc) + : sndr(payloadsize_loc) + , rcvr(payloadsize_loc) + {} } m_stats; public: @@ -1168,6 +1179,7 @@ class CUDT sockaddr_any m_PeerAddr; // peer address sockaddr_any m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address + int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f4dfba1ba..c411740cd 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -236,7 +236,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); if (plsize == 0) - plsize = SRT_LIVE_MAX_PLSIZE; + plsize = CPacket::srtPayloadSize(data.agent.family()); // It is stated that the payload size // is taken from first, and every next one // will get the same. @@ -506,8 +506,8 @@ void CUDTGroup::deriveSettings(CUDT* u) IM(SRTO_FC, iFlightFlagSize); // Nonstandard - 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)); + importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); + importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); IM(SRTO_LINGER, Linger); IM(SRTO_UDP_SNDBUF, iUDPSndBufSize); @@ -639,7 +639,7 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) case SRTO_SNDBUF: case SRTO_RCVBUF: - w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::UDP_HDR_SIZE)); + w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::udpHeaderSize(AF_INET))); break; case SRTO_LINGER: @@ -3533,7 +3533,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Only live streaming is supported - if (len > SRT_LIVE_MAX_PLSIZE) + // Also - as the group may use potentially IPv4 and IPv6 connections + // in the same group, use the size that fits both + if (len > SRT_MAX_PLSIZE_AF_INET6) { LOGC(gslog.Error, log << "grp/send(backup): buffer size=" << len << " exceeds maximum allowed in live mode"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -3968,7 +3970,8 @@ void CUDTGroup::internalKeepalive(SocketData* gli) } } -CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_LIVE_MAX_PLSIZE /*, 1000*/); +// Use the bigger size of SRT_MAX_PLSIZE to potentially fit both IPv4/6 +CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_MAX_PLSIZE_AF_INET /*, 1000*/); // Forwarder needed due to class definition order int32_t CUDTGroup::generateISN() diff --git a/srtcore/group_backup.h b/srtcore/group_backup.h index 790cf55ed..cecbc6d1b 100644 --- a/srtcore/group_backup.h +++ b/srtcore/group_backup.h @@ -79,6 +79,7 @@ namespace groups : m_stateCounter() // default init with zeros , m_activeMaxWeight() , m_standbyMaxWeight() + , m_rateEstimate(AF_INET6) // XXX Probably the whole solution is wrong { } diff --git a/srtcore/packet.h b/srtcore/packet.h index 027d5f0b3..b7399b925 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -364,16 +364,24 @@ class CPacket static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH_E_SIZE * sizeof(uint32_t) - // Can also be calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr). - static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. - - static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE; +private: // Do not disclose ingredients to the public + static const size_t UDP_HDR_SIZE = 8; // 8 bytes of UDP { u16 sport, dport, len, csum }. + static const size_t IPv4_HDR_SIZE = 20; // 20 bytes IPv4 + static const size_t IPv6_HDR_SIZE = 32; // 32 bytes IPv6 +public: + static inline size_t udpHeaderSize(int family) + { + return UDP_HDR_SIZE + (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE); + } + static inline size_t srtPayloadSize(int family) + { + return ETH_MAX_MTU_SIZE - (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE) - UDP_HDR_SIZE - HDR_SIZE; + } // Maximum transmission unit size. 1500 in case of Ethernet II (RFC 1191). static const size_t ETH_MAX_MTU_SIZE = 1500; // Maximum payload size of an SRT packet. - static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE; // Packet interface char* data() { return m_pcData; } diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 3bfba7c76..ef0d8867f 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -66,7 +66,7 @@ struct SrtFilterInitializer struct SrtPacket { uint32_t hdr[SRT_PH_E_SIZE]; - char buffer[SRT_LIVE_MAX_PLSIZE]; + char buffer[SRT_MAX_PLSIZE_AF_INET]; // Using this as the bigger one (this for AF_INET6 is smaller) size_t length; SrtPacket(size_t size): length(size) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 476162001..e72563c27 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -70,7 +70,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + if (ival < int(CPacket::udpHeaderSize(AF_INET6) + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iMSS = ival; @@ -109,7 +109,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 - CPacket::udpHeaderSize(AF_INET)); } }; @@ -123,7 +123,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 - CPacket::udpHeaderSize(AF_INET); if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; @@ -609,42 +609,20 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (val > SRT_LIVE_MAX_PLSIZE) + // We don't know at this point, how bit the payloadsize can be set, + // so we limit it to the biggest value of the two. + // When this payloadsize would be then too big to be used with given MSS and IPv6, + // this problem should be reported appropriately from srt_connect and srt_bind calls. + if (val > SRT_MAX_PLSIZE_AF_INET) { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU."); + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_MAX_PLSIZE_AF_INET << ", maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (!co.sPacketFilterConfig.empty()) + std::string errorlog; + if (!co.payloadSizeFits(size_t(val), AF_INET, (errorlog))) { - // 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)) - { - // 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 << " bytes decreased by " << fc.extra_size - << " required for packet filter header"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - // Not checking AUTO to allow defaul 1456 bytes. - if ((co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX))) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."); + LOGP(aclog.Error, errorlog); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -840,7 +818,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET - fc.extra_size; if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Warn, @@ -1011,6 +989,47 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + if (!this->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 (!this->sPacketFilterConfig.empty() && !ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) + { + // Break silently. This should not happen + w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return false; + } + + const size_t efc_max_payload_size = CPacket::srtPayloadSize(ip_family) - fc.extra_size; + if (size_t(val) > efc_max_payload_size) + { + std::ostringstream log; + log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) << " bytes decreased by " << fc.extra_size + << " required for packet filter header"; + w_errmsg = log.str(); + return false; + } + } + + // Not checking AUTO to allow defaul 1456 bytes. + if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + && (val > (CPacket::srtPayloadSize(ip_family) - HAICRYPT_AUTHTAG_MAX))) + { + std::ostringstream log; + log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) + << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX + << " required for AES-GCM."; + w_errmsg = log.str(); + return false; + } + + return true; +} + #if ENABLE_BONDING bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t optlen) { diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 488d12fb1..72ae9808f 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -326,6 +326,8 @@ struct CSrtConfig: CSrtMuxerConfig } int set(SRT_SOCKOPT optName, const void* val, int size); + + bool payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 39a90ce71..784274f86 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -299,7 +299,12 @@ static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS // This is the maximum payload size for Live mode, should you have a different // payload type than MPEG TS. -static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) +SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) + +static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) +static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) + +#define SRT_MAX_PLSIZE(famspec) ((famspec) == AF_INET ? SRT_MAX_PLSIZE_AF_INET : SRT_MAX_PLSIZE_AF_INET6) // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; diff --git a/srtcore/stats.h b/srtcore/stats.h index bce60761b..2c0276665 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -22,6 +22,8 @@ namespace stats class Packets { public: + typedef Packets count_type; + Packets() : m_count(0) {} Packets(uint32_t num) : m_count(num) {} @@ -46,27 +48,20 @@ class Packets uint32_t m_count; }; -class BytesPackets +class BytesPacketsCount { public: - BytesPackets() + BytesPacketsCount() : m_bytes(0) , m_packets(0) {} - BytesPackets(uint64_t bytes, uint32_t n = 1) + BytesPacketsCount(uint64_t bytes, uint32_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; @@ -89,28 +84,63 @@ class BytesPackets return m_packets; } - uint64_t bytesWithHdr() const + BytesPacketsCount& operator+= (const BytesPacketsCount& other) { - return m_bytes + m_packets * CPacket::SRT_DATA_HDR_SIZE; + m_bytes += other.m_bytes; + m_packets += other.m_packets; + return *this; } -private: +protected: uint64_t m_bytes; uint32_t m_packets; }; -template +class BytesPackets: public BytesPacketsCount +{ +public: + typedef BytesPacketsCount count_type; + + BytesPackets() + : m_pPayloadSizeLocation(NULL) + {} + +public: + + void bindPayloadSize(int* sizeloc) + { + m_pPayloadSizeLocation = sizeloc; + } + + uint64_t bytesWithHdr() const + { + SRT_ASSERT(m_pPayloadSizeLocation != NULL); + size_t header_size = CPacket::ETH_MAX_MTU_SIZE - *m_pPayloadSizeLocation; + return m_bytes + m_packets * header_size; + } + +private: + int* m_pPayloadSizeLocation; +}; + +template struct Metric { METRIC_TYPE trace; METRIC_TYPE total; - void count(METRIC_TYPE val) + void count(typename METRIC_TYPE::count_type val) { trace += val; total += val; } + void bindPayloadSize(int* loc) + { + trace.bindPayloadSize(loc); + total.bindPayloadSize(loc); + } + void reset() { trace.reset(); @@ -137,6 +167,16 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. + Sender(int* payloadsize_loc) + { +#define BIND(var) var.bindPayloadSize(payloadsize_loc) + BIND(sent); + BIND(sentUnique); + BIND(sentRetrans); + BIND(dropped); +#undef BIND + } + void reset() { sent.reset(); @@ -180,6 +220,19 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. + Receiver(int* payloadsize_loc) + { +#define BIND(var) var.bindPayloadSize(payloadsize_loc) + BIND(recvd); + BIND(recvdUnique); + BIND(recvdRetrans); + BIND(lost); + BIND(dropped); + BIND(recvdBelated); + BIND(undecrypted); +#undef BIND + } + void reset() { recvd.reset(); diff --git a/srtcore/window.cpp b/srtcore/window.cpp index 46889ecb0..b038575e4 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -145,7 +145,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 //////////////////////////////////////////////////////////////////////////////// -void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize) +void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize, size_t max_payload_size) { for (size_t i = 0; i < asize; ++ i) r_pktWindow[i] = 1000000; //1 sec -> 1 pkt/sec @@ -154,11 +154,11 @@ void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_p r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec for (size_t i = 0; i < asize; ++ i) - r_bytesWindow[i] = srt::CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] + r_bytesWindow[i] = max_payload_size; //based on 1 pkt/sec set in r_pktWindow[i] } -int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps) +int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, size_t hdr_size, int& bytesps) { // get median value, but cannot change the original value order in the window std::copy(window, window + asize, replica); @@ -191,7 +191,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, // claculate speed, or return 0 if not enough valid value if (count > (asize >> 1)) { - bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received + bytes += (hdr_size * count); //Add protocol headers to bytes received bytesps = (int)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 ecc4a4947..3dfd94c3e 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -130,10 +130,10 @@ class CACKWindow class CPktTimeWindowTools { public: - static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, int& bytesps); + static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, size_t hsize, int& bytesps); static int getBandwidth_in(const int* window, int* replica, size_t psize); - static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize); + static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize, size_t max_payload_size); }; template @@ -151,12 +151,13 @@ class CPktTimeWindow: CPktTimeWindowTools m_tsLastArrTime(sync::steady_clock::now()), m_tsCurrArrTime(), m_tsProbeTime(), - m_Probe1Sequence(SRT_SEQNO_NONE) + m_Probe1Sequence(SRT_SEQNO_NONE), + m_zPayloadSize(0), + m_zHeaderSize(0) { // Exception: up to CUDT ctor sync::setupMutex(m_lockPktWindow, "PktWindow"); sync::setupMutex(m_lockProbeWindow, "ProbeWindow"); - CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE); } ~CPktTimeWindow() @@ -174,11 +175,12 @@ class CPktTimeWindow: CPktTimeWindowTools int getPktRcvSpeed(int& w_bytesps) const { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Lock access to the packet Window sync::ScopedLock cg(m_lockPktWindow); int pktReplica[ASIZE]; // packet information window (inter-packet time) - return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, (w_bytesps)); + return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, m_zHeaderSize, (w_bytesps)); } int getPktRcvSpeed() const @@ -192,6 +194,7 @@ class CPktTimeWindow: CPktTimeWindowTools int getBandwidth() const { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Lock access to the packet Window sync::ScopedLock cg(m_lockProbeWindow); @@ -204,6 +207,7 @@ class CPktTimeWindow: CPktTimeWindowTools void onPktSent(int currtime) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); int interval = currtime - m_iLastSentTime; if ((interval < m_iMinPktSndInt) && (interval > 0)) @@ -216,6 +220,7 @@ class CPktTimeWindow: CPktTimeWindowTools void onPktArrival(int pktsz = 0) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); sync::ScopedLock cg(m_lockPktWindow); m_tsCurrArrTime = sync::steady_clock::now(); @@ -236,6 +241,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet @@ -257,6 +263,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) { // Reset the starting probe into "undefined", when @@ -274,6 +281,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probe2Arrival(const CPacket& pkt) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Reject probes that don't refer to the very next packet // towards the one that was lately notified by probe1Arrival. // Otherwise the result can be stupid. @@ -302,7 +310,7 @@ class CPktTimeWindow: CPktTimeWindowTools // record the probing packets interval // Adjust the time for what a complete packet would have take const int64_t timediff = 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 * m_zPayloadSize; // Let's take it simpler than it is coded here: // (stating that a packet has never zero size) @@ -327,6 +335,14 @@ class CPktTimeWindow: CPktTimeWindowTools m_iProbeWindowPtr = 0; } + // Late initialization because these sizes aren't known at construction time of the parent CUDT class. + void initialize(size_t h, size_t s) + { + m_zHeaderSize = h; + m_zPayloadSize = s; + CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE, s); + } + private: int m_aPktWindow[ASIZE]; // Packet information window (inter-packet time) int m_aBytesWindow[ASIZE]; @@ -345,6 +361,9 @@ class CPktTimeWindow: CPktTimeWindowTools 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 + size_t m_zPayloadSize; + size_t m_zHeaderSize; + private: CPktTimeWindow(const CPktTimeWindow&); CPktTimeWindow &operator=(const CPktTimeWindow&); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..bd50c6ffb 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -58,7 +58,7 @@ class TestFECRebuilding: public testing::Test source.emplace_back(new CPacket); CPacket& p = *source.back(); - p.allocate(SRT_LIVE_MAX_PLSIZE); + p.allocate(SRT_MAX_PLSIZE_AF_INET); uint32_t* hdr = p.getHeader(); @@ -729,7 +729,7 @@ TEST_F(TestFECRebuilding, Prepare) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -750,7 +750,7 @@ TEST_F(TestFECRebuilding, NoRebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -827,7 +827,7 @@ TEST_F(TestFECRebuilding, Rebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 5a646fb7d..48d790471 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -17,6 +17,7 @@ #endif #include "srt.h" +#include "netinet_any.h" #include #include @@ -27,7 +28,7 @@ //#pragma comment (lib, "ws2_32.lib") -TEST(Transmission, FileUpload) +TEST(FileTransmission, Upload) { srt_startup(); @@ -197,3 +198,113 @@ TEST(Transmission, FileUpload) (void)srt_cleanup(); } + +TEST(FileTransmission, Setup46) +{ + using namespace srt; + + srt_startup(); + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + try + { + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + // srt_setloglevel(LOG_DEBUG); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); + + + srt_setloglevel(LOG_DEBUG); + + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_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_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + unsigned int randseed = std::time(NULL); + + for (size_t i = 0; i < SIZE; ++i) + buffer[i] = rand_r(&randseed); + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + + } + catch (...) + { + srt_cleanup(); + throw; + } + + srt_cleanup(); +} + +// XXX Setup66 - establish an IPv6 to IPv6 connection and make sure max payload size is that of IPv6. diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index c23b262b7..90baae7f6 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -240,7 +240,7 @@ int main( int argc, char** argv ) return 2; } - size_t chunk = SRT_LIVE_MAX_PLSIZE; + size_t chunk = SRT_MAX_PLSIZE_AF_INET; // state the bigger size // Now run the loop try diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 34d2bdc9b..b30d98a20 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -392,6 +392,37 @@ void SrtCommon::InitParameters(string host, string path, map par) { m_mode = par.at("mode"); } + + int fam_to_limit_size = AF_INET6; // take the less one as default + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + fam_to_limit_size = host_sa.family(); + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + } + + if (adapter != "") + { + adapter_sa = CreateAddr(adapter); + fam_to_limit_size = adapter_sa.family(); + + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + } + + if (fam_to_limit_size != AF_INET) + fam_to_limit_size = AF_INET6; + SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) { @@ -445,16 +476,14 @@ void SrtCommon::InitParameters(string host, string path, map par) // That's kinda clumsy, but it must rely on the defaults. // Default mode is live, so check if the file mode was enforced - if (par.count("transtype") == 0 || par["transtype"] != "file") + if ((par.count("transtype") == 0 || par["transtype"] != "file") + && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - // If the Live chunk size was nondefault, enforce the size. - if (transmit_chunk_size != SRT_LIVE_DEF_PLSIZE) - { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); + if (transmit_chunk_size > size_limit) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); - par["payloadsize"] = Sprint(transmit_chunk_size); - } + par["payloadsize"] = Sprint(transmit_chunk_size); } // Assigning group configuration from a special "groupconfig" attribute. From a8b91042845744053fa339937b6f437973d46591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 17:52:02 +0100 Subject: [PATCH 037/174] Applied verification of the payload size fit. Added tests. Updated documentation --- docs/API/API-functions.md | 16 +++ docs/API/API-socket-options.md | 256 +++++++++++++++++++++++---------- srtcore/api.cpp | 8 +- srtcore/core.cpp | 48 +++++-- srtcore/core.h | 17 ++- srtcore/socketconfig.cpp | 4 +- srtcore/socketconfig.h | 4 + srtcore/srt.h | 1 + srtcore/stats.h | 53 ++++--- test/test_ipv6.cpp | 144 ++++++++++++++++++- 10 files changed, 417 insertions(+), 134 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 4292a99b5..d4c4de970 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -172,6 +172,7 @@ Since SRT v1.5.0. | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.6.0-dev | The connection was rejected due to an unsupported or mismatching encryption mode | +| [SRT_REJ_SETTINGS](#SRT_REJ_SETTINGS) | 1.6.0 | The connection was rejected because settings on both parties are in collision and cannot negotiate common values | | | | |

Error Codes

@@ -3064,6 +3065,21 @@ and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by the application. +#### SRT_REJ_CRYPTO + +Settings for `SRTO_CRYPTOMODE` on both parties are not compatible with one another. +See [`SRTO_CRYPTOMODE`](API-socket-options.md#SRTO_CRYPTOMODE) for details. + +#### SRT_REJ_SETTINGS + +Settings for various transmission parameters that are supposed to be negotiated +during the handshake (in order to agree upon a common value) are under restrictions +that make finding common values for them impossible. Cases include: + +* `SRTO_PAYLOADSIZE`, which is nonzero in live mode, is set to a value that +exceeds the free space in a single packet that results from the value of the +negotiated MSS value + [: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 5d32db4ca..2730dcc92 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -264,7 +264,7 @@ The following table lists SRT API socket options in alphabetical order. Option d ### Option Descriptions -#### SRTO_BINDTODEVICE +#### `SRTO_BINDTODEVICE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir |Entity| | --------------------- | ----- | -------- | -------- | ------ | -------- | ------ |-----|------| @@ -287,7 +287,7 @@ for a process that runs as root. Otherwise the function that applies the setting --- -#### SRTO_CONGESTION +#### `SRTO_CONGESTION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -309,7 +309,7 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST --- -#### SRTO_CONNTIMEO +#### `SRTO_CONNTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -323,7 +323,7 @@ will be 10 times the value set with `SRTO_CONNTIMEO`. --- -#### SRTO_CRYPTOMODE +#### `SRTO_CRYPTOMODE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | --------- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -377,7 +377,7 @@ There is no way to check the crypto mode being requested by the SRT caller at th --- -#### SRTO_DRIFTTRACER +#### `SRTO_DRIFTTRACER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -389,7 +389,7 @@ Enables or disables time drift tracer (receiver). --- -#### SRTO_ENFORCEDENCRYPTION +#### `SRTO_ENFORCEDENCRYPTION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -434,7 +434,7 @@ on the caller side. --- -#### SRTO_EVENT +#### `SRTO_EVENT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -449,7 +449,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of --- -#### SRTO_FC +#### `SRTO_FC` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -488,7 +488,7 @@ where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCV --- -#### SRTO_GROUPCONNECT +#### `SRTO_GROUPCONNECT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -514,7 +514,7 @@ function will return the group, not this socket ID. --- -#### SRTO_GROUPMINSTABLETIMEO +#### `SRTO_GROUPMINSTABLETIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -548,7 +548,7 @@ Note that the value of this option is not allowed to exceed the value of --- -#### SRTO_GROUPTYPE +#### `SRTO_GROUPTYPE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -567,7 +567,7 @@ context than inside the listener callback handler, the value is undefined. --- -#### SRTO_INPUTBW +#### `SRTO_INPUTBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -588,7 +588,7 @@ and keep the default 25% value for `SRTO_OHEADBW`*. --- -#### SRTO_MININPUTBW +#### `SRTO_MININPUTBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -603,7 +603,7 @@ See [`SRTO_INPUTBW`](#SRTO_INPUTBW). --- -#### SRTO_IPTOS +#### `SRTO_IPTOS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -621,7 +621,7 @@ and the actual value for connected sockets. --- -#### SRTO_IPTTL +#### `SRTO_IPTTL` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -639,7 +639,7 @@ and the actual value for connected sockets. --- -#### SRTO_IPV6ONLY +#### `SRTO_IPV6ONLY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -661,7 +661,7 @@ reliable way). Possible values are: --- -#### SRTO_ISN +#### `SRTO_ISN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -678,7 +678,7 @@ used in any regular development.* --- -#### SRTO_KMPREANNOUNCE +#### `SRTO_KMPREANNOUNCE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | @@ -712,7 +712,7 @@ The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. --- -#### SRTO_KMREFRESHRATE +#### `SRTO_KMREFRESHRATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | @@ -734,7 +734,7 @@ might still be in flight, or packets that have to be retransmitted. --- -#### SRTO_KMSTATE +#### `SRTO_KMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -752,7 +752,7 @@ for more details. --- -#### SRTO_LATENCY +#### `SRTO_LATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -771,7 +771,7 @@ be sender and receiver at the same time, and `SRTO_SENDER` became redundant. --- -#### SRTO_LINGER +#### `SRTO_LINGER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -789,7 +789,7 @@ The default value in [the file transfer configuration](./API.md#transmission-typ --- -#### SRTO_LOSSMAXTTL +#### `SRTO_LOSSMAXTTL` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -810,7 +810,7 @@ By default this value is set to 0, which means that this mechanism is off. --- -#### SRTO_MAXBW +#### `SRTO_MAXBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -834,7 +834,7 @@ therefore the default -1 remains even in live mode. --- -#### SRTO_MESSAGEAPI +#### `SRTO_MESSAGEAPI` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -873,7 +873,7 @@ SCTP protocol. --- -#### SRTO_MINVERSION +#### `SRTO_MINVERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -889,30 +889,91 @@ The default value is 0x010000 (SRT v1.0.0). --- -#### SRTO_MSS +#### `SRTO_MSS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `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 -value independently. During a handshake the parties exchange MSS values, and -the lowest is used. - -*Generally on the internet MSS is 1500 by default. This is the maximum -size of a UDP packet and can be only decreased, unless you have some unusual -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`. +Maximum Segment Size. This value represents the maximum size of a UDP packet +sent by the system. Therefore the value of `SRTO_MSS` must not exceed the +values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer +allocation and rate calculation using packet counter assuming fully filled +packets. + +This value is a sum of: + +* IP header (20 bytes for IPv4 or 32 bytes for IPv6) +* UDP header (8 bytes) +* SRT header (16 bytes) +* remaining space (as the maximum payload size available for a packet) + +For the default 1500 the "remaining space" part is effectively 1456 for IPv4 +and 1444 for IPv6. + +Note that the IP version used here is not the domain of the socket, but the +in-transmission IP version. This is IPv4 for a case when the current socket's +binding address is of IPv4 domain, or if it is IPv6, but the peer's address +is then an IPv6-mapped-IPv4 address. The in-transmission IPv6 is only if the +peer's address is a true IPv6 address. Hence it is not possible to deteremine +all these limitations until the connection is established. Parts of SRT that +must allocate any resources regarding this value are using the layout as per +IPv4 because this results in a greater size of "remaining space". + +This value can be set on both connection parties independently, but after +connection this option gets an effectively negotiated value, which is the less +one from both parties. + +This value then effectively controls: + +* The maximum size of the data in a single UDP packet ("remaining space"). + +* The size of the memory space allocated for a single packet in the sender +and receiver buffers. This value is equal to "SRT header" + "remaining space" +in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it +is that some buffer resources are allocated prior to the connection, so this +value must fit both IPv4 and IPv6 for buffer memory allocation. + +The default value 1500 matches the standard MTU size for network devices. It +is recommended that this value be set at maximum to the value of MTU size of +the network device that you will use for connection. + +Detailed recommendations for this value differ in the file and live mode. + +In the live mode a single call to `srt_send*` function may only send data +that fit in one packet. This size is defined by the `SRTO_PAYLOADSIZE` +option (defult: 1316) and it is also the size of the data in a single UDP +packet. To save memory space, you may want then to set MSS in live mode to +a value for which the "remaining space" matches `SRTO_PAYLOADSIZE` value (for +default 1316 it will be 1360 for IPv4 and 1372 for IPv6). This is not done by +default for security reasons: this may potentially lead to inability to read an +incoming UDP packet if its size is by some reason bigger than the negotiated MSS. +This may lead to misleading situations and hard to detect errors. You should +set such a value only if the peer is trusted (that is, you can be certain that +it will never come to a situation of having received an oversized UDP packet +over the link used for the connection). See also limitations for +`SRTO_PAYLOADSIZE`. + +In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for one single packet sending, and therefore bigger portions of data are +internally split into smaller portions, each one using the maximum available +"remaining space". The best value for this case is then equal to the current +network device's MTU size. Setting a greater value is possible (maximum for the +system API is 65535), but it may lead to packet fragmentation on the system +level. This is highly unwanted in SRT because: + +* SRT does also its own fragmentation, so it would be counter-productive +* It would use more system resources with no advantage +* SRT is unaware of it, so the statistics will be slightly falsified + +The system-level packet fragmentation cannot be however reliably turned off; +the best approach is then to avoid it by using appropriate parameters. [Return to list](#list-of-options) --- -#### SRTO_NAKREPORT +#### `SRTO_NAKREPORT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -929,7 +990,7 @@ The default is true for Live mode, and false for File mode (see [`SRTO_TRANSTYPE --- -#### SRTO_OHEADBW +#### `SRTO_OHEADBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -959,7 +1020,7 @@ and break quickly at any rise in packet loss. --- -#### SRTO_PACKETFILTER +#### `SRTO_PACKETFILTER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1003,7 +1064,7 @@ Cases when negotiation succeeds: | 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: +In these cases the configuration is rejected with `SRT_REJ_FILTER` code: | Peer A | Peer B | Error reason |-----------------------|---------------------|-------------------------- @@ -1025,7 +1086,7 @@ For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-f --- -#### SRTO_PASSPHRASE +#### `SRTO_PASSPHRASE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1054,18 +1115,57 @@ encrypted connection, they have to simply set the same passphrase. --- -#### SRTO_PAYLOADSIZE +#### `SRTO_PAYLOADSIZE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `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. +Sets the data limitation mode and the maximum data size for sending at once. + +The default value is 0 in the file mode and 1316 in live mode (this is one of +the options modified together with `SRTO_TRANSTYPE`). + +If the value is 0, this means a "file mode", in which the call to `srt_send*` +is not limited to a size fitting in one single packet, that is, the supplied +data will be split into multiple pieces fitting in a single UDP packet, if +necessary, as well as every packet will use the maximum space available +in a UDP packet (except the last in the stream or in the message) according to +the `SRTO_MSS` setting and others that may influence this size (such as +`SRTO_PACKETFILTER` and `SRTO_CRYPTOMODE`). + +If the value is greater than 0, this means a "live mode", and the value +defines the maximum size of: + +* the single call to a sending function (`srt_send*`) +* the payload supplied in every single data packet + +This value can be set to a greater value than the default 1316, but the maximum +possible value is limited by the following factors: + +* 1500 is the default MSS (see `SRTO_MSS`), including headers, which are: + * 20 bytes for IPv4 or 32 bytes for IPv6 + * 8 bytes for UDP + * 16 bytes for SRT + +This alone gives the limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be however further decreased in the following cases: + +* 4 bytes reserved for FEC, if you use the builtin FEC packet filter (see `SRTO_PACKETFILTER`) +* 16 bytes reserved for authentication tag, if you use AES GCM (see `SRTO_CRYPTOMODE`) + +**WARNING**: The option setter will reject the setting if this value is too +great, but note that not every limitation can be checked prior to connection. +This includes: + +* MSS defined for the peer, which may override MSS set in the agent +* The in-transmission IP version - see `SRTO_MSS` for details -For Live mode: Default value is 1316, but can be increased up to 1456. Note that -with the `SRTO_PACKETFILTER` option additional header space is usually required, -which decreases the maximum possible value for `SRTO_PAYLOADSIZE`. +These values also influence the "remaining space" in the packet to be used for +payload. If during the handshake it turns out that this "remaining space" is +less than the value set for `SRTO_PAYLOADSIZE` (including when it remains with +the default value), the connection will be rejected with the `SRT_REJ_SETTINGS` +code. For File mode: Default value is 0 and it's recommended not to be changed. @@ -1073,7 +1173,7 @@ For File mode: Default value is 0 and it's recommended not to be changed. --- -#### SRTO_PBKEYLEN +#### `SRTO_PBKEYLEN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1160,7 +1260,7 @@ undefined behavior: --- -#### SRTO_PEERIDLETIMEO +#### `SRTO_PEERIDLETIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1174,7 +1274,7 @@ considered broken on timeout. --- -#### SRTO_PEERLATENCY +#### `SRTO_PEERLATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1195,7 +1295,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### SRTO_PEERVERSION +#### `SRTO_PEERVERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1209,7 +1309,7 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. --- -#### SRTO_RCVBUF +#### `SRTO_RCVBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1229,7 +1329,7 @@ than the Flight Flag size). --- -#### SRTO_RCVDATA +#### `SRTO_RCVDATA` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1241,7 +1341,7 @@ Size of the available data in the receive buffer. --- -#### SRTO_RCVKMSTATE +#### `SRTO_RCVKMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1255,7 +1355,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### SRTO_RCVLATENCY +#### `SRTO_RCVLATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1297,7 +1397,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### SRTO_RCVSYN +#### `SRTO_RCVSYN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1334,7 +1434,7 @@ derived from the socket of which the group is a member). --- -#### SRTO_RCVTIMEO +#### `SRTO_RCVTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1348,7 +1448,7 @@ it will behave as if in "non-blocking mode". The -1 value means no time limit. --- -#### SRTO_RENDEZVOUS +#### `SRTO_RENDEZVOUS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1361,7 +1461,7 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot --- -#### SRTO_RETRANSMITALGO +#### `SRTO_RETRANSMITALGO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | @@ -1389,7 +1489,7 @@ Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). --- -#### SRTO_REUSEADDR +#### `SRTO_REUSEADDR` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1416,7 +1516,7 @@ its address.* --- -#### SRTO_SENDER +#### `SRTO_SENDER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1436,7 +1536,7 @@ parties simultaneously. --- -#### SRTO_SNDBUF +#### `SRTO_SNDBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1448,7 +1548,7 @@ Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. --- -#### SRTO_SNDDATA +#### `SRTO_SNDDATA` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1460,7 +1560,7 @@ Size of the unacknowledged data in send buffer. --- -#### SRTO_SNDDROPDELAY +#### `SRTO_SNDDROPDELAY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1487,7 +1587,7 @@ always when requested). --- -#### SRTO_SNDKMSTATE +#### `SRTO_SNDKMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1501,7 +1601,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### SRTO_SNDSYN +#### `SRTO_SNDSYN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1527,7 +1627,7 @@ but will have no effect on the listener socket itself. --- -#### SRTO_SNDTIMEO +#### `SRTO_SNDTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1541,7 +1641,7 @@ if in "non-blocking mode". The -1 value means no time limit. --- -#### SRTO_STATE +#### `SRTO_STATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1553,7 +1653,7 @@ Returns the current socket state, same as `srt_getsockstate`. --- -#### SRTO_STREAMID +#### `SRTO_STREAMID` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1582,7 +1682,7 @@ influence anything. --- -#### SRTO_TLPKTDROP +#### `SRTO_TLPKTDROP` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1601,7 +1701,7 @@ enabled in sender if receiver supports it. --- -#### SRTO_TRANSTYPE +#### `SRTO_TRANSTYPE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1617,7 +1717,7 @@ Values defined by enum `SRT_TRANSTYPE` (see above for possible values) --- -#### SRTO_TSBPDMODE +#### `SRTO_TSBPDMODE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1635,7 +1735,7 @@ the application. --- -#### SRTO_UDP_RCVBUF +#### `SRTO_UDP_RCVBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1648,7 +1748,7 @@ based on MSS value. Receive buffer must not be greater than FC size. --- -#### SRTO_UDP_SNDBUF +#### `SRTO_UDP_SNDBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1661,7 +1761,7 @@ on `SRTO_MSS` value. --- -#### SRTO_VERSION +#### `SRTO_VERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4a22cb8ec..e2dd288b7 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -618,7 +618,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // bind to the same addr of listening socket - ns->core().open(peer.family()); + ns->core().open(); if (!updateListenerMux(ns, ls)) { // This is highly unlikely if not impossible, but there's @@ -928,7 +928,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - s->core().open(name.family()); + s->core().open(); updateMux(s, name); s->m_Status = SRTS_OPENED; @@ -957,7 +957,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->core().open(name.family()); + s->core().open(); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; @@ -1847,7 +1847,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->core().open(target_addr.family()); + s->core().open(); sockaddr_any autoselect_sa(target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b4d6160cf..c66492375 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -308,7 +308,6 @@ void srt::CUDT::construct() srt::CUDT::CUDT(CUDTSocket* parent) : m_parent(parent) - , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -333,7 +332,6 @@ srt::CUDT::CUDT(CUDTSocket* parent) srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor) : m_parent(parent) - , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -478,12 +476,12 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) // checking if the socket is bound. This will also be the exact size // of the memory in use. case SRTO_SNDBUF: - *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); + *(int *)optval = m_config.iSndBufSize * m_config.bytesPerPkt(); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); + *(int *)optval = m_config.iRcvBufSize * m_config.bytesPerPkt(); optlen = sizeof(int); break; @@ -881,9 +879,9 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. // Initial sequence number, loss, acknowledgement, etc. -void srt::CUDT::clearData(int family) +void srt::CUDT::clearData() { - const size_t full_hdr_size = CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -932,11 +930,11 @@ void srt::CUDT::clearData(int family) m_tsRcvPeerStartTime = steady_clock::time_point(); } -void srt::CUDT::open(int family) +void srt::CUDT::open() { ScopedLock cg(m_ConnectionLock); - clearData(family); + clearData(); // structures for queue if (m_pSNode == NULL) @@ -4185,7 +4183,7 @@ EConnectStatus srt::CUDT::processRendezvous( // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers. if (!applyResponseSettings(pResponse)) { - LOGC(cnlog.Error, log << CONID() << "processRendezvous: rogue peer"); + LOGC(cnlog.Error, log << CONID() << "processRendezvous: peer settings rejected"); return CONN_REJECT; } @@ -4685,6 +4683,21 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; + + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_SETTINGS; + return false; + } + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_stats.setupHeaderSize(full_hdr_size); + + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -5676,6 +5689,20 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // Uses the smaller MSS between the peers m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS); + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_SETTINGS; + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_stats.setupHeaderSize(full_hdr_size); + // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; m_iPeerISN = w_hs.m_iISN; @@ -5706,9 +5733,6 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& m_TransferIPVersion = AF_INET; } - const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; - m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; - HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); // Prepare all structures if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0)) diff --git a/srtcore/core.h b/srtcore/core.h index 67ee0dcb7..ee79e3442 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -179,7 +179,7 @@ class CUDT private: // constructor and desctructor void construct(); - void clearData(int family); + void clearData(); CUDT(CUDTSocket* parent); CUDT(CUDTSocket* parent, const CUDT& ancestor); const CUDT& operator=(const CUDT&) {return *this;} // = delete ? @@ -341,6 +341,7 @@ class CUDT // then return the maximum payload size per packet. return m_iMaxSRTPayloadSize; } + int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -459,7 +460,7 @@ class CUDT private: /// initialize a UDT entity and bind to a local address. - void open(int family); + void open(); /// Start listening to any connection request. void setListenState(); @@ -1137,14 +1138,16 @@ class CUDT time_point tsLastSampleTime; // last performance sample time int traceReorderDistance; double traceBelatedTime; - + int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration - CoreStats(int* payloadsize_loc) - : sndr(payloadsize_loc) - , rcvr(payloadsize_loc) - {} + void setupHeaderSize(int hsize) + { + sndr.setupHeaderSize(hsize); + rcvr.setupHeaderSize(hsize); + } + } m_stats; public: diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index e72563c27..27eda173b 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -109,7 +109,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::udpHeaderSize(AF_INET)); + co.iSndBufSize = bs / co.bytesPerPkt(); } }; @@ -123,7 +123,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - CPacket::udpHeaderSize(AF_INET); + const int mssin_size = co.bytesPerPkt(); if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 72ae9808f..bd222b327 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -328,6 +328,10 @@ struct CSrtConfig: CSrtMuxerConfig int set(SRT_SOCKOPT optName, const void* val, int size); bool payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW; + + // This function returns the number of bytes that are allocated + // for a single packet in the sender and receiver buffer. + int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 784274f86..939f7f9d0 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -564,6 +564,7 @@ enum SRT_REJECT_REASON #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif + SRT_REJ_SETTINGS, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; diff --git a/srtcore/stats.h b/srtcore/stats.h index 2c0276665..14b0d131e 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -101,26 +101,25 @@ class BytesPackets: public BytesPacketsCount public: typedef BytesPacketsCount count_type; + // Set IPv4-based header size value as a fallback. This will be fixed upon connection. BytesPackets() - : m_pPayloadSizeLocation(NULL) + : m_iPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) {} public: - void bindPayloadSize(int* sizeloc) + void setupHeaderSize(int size) { - m_pPayloadSizeLocation = sizeloc; + m_iPacketHeaderSize = size; } uint64_t bytesWithHdr() const { - SRT_ASSERT(m_pPayloadSizeLocation != NULL); - size_t header_size = CPacket::ETH_MAX_MTU_SIZE - *m_pPayloadSizeLocation; - return m_bytes + m_packets * header_size; + return m_bytes + m_packets * m_iPacketHeaderSize; } private: - int* m_pPayloadSizeLocation; + int m_iPacketHeaderSize; }; template @@ -135,10 +134,10 @@ struct Metric total += val; } - void bindPayloadSize(int* loc) + void setupHeaderSize(int loc) { - trace.bindPayloadSize(loc); - total.bindPayloadSize(loc); + trace.setupHeaderSize(loc); + total.setupHeaderSize(loc); } void reset() @@ -167,14 +166,14 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - Sender(int* payloadsize_loc) + void setupHeaderSize(int hdr_size) { -#define BIND(var) var.bindPayloadSize(payloadsize_loc) - BIND(sent); - BIND(sentUnique); - BIND(sentRetrans); - BIND(dropped); -#undef BIND +#define SETHSIZE(var) var.setupHeaderSize(hdr_size) + SETHSIZE(sent); + SETHSIZE(sentUnique); + SETHSIZE(sentRetrans); + SETHSIZE(dropped); +#undef SETHSIZE } void reset() @@ -220,17 +219,17 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - Receiver(int* payloadsize_loc) + void setupHeaderSize(int hdr_size) { -#define BIND(var) var.bindPayloadSize(payloadsize_loc) - BIND(recvd); - BIND(recvdUnique); - BIND(recvdRetrans); - BIND(lost); - BIND(dropped); - BIND(recvdBelated); - BIND(undecrypted); -#undef BIND +#define SETHSIZE(var) var.setupHeaderSize(hdr_size) + SETHSIZE(recvd); + SETHSIZE(recvdUnique); + SETHSIZE(recvdRetrans); + SETHSIZE(lost); + SETHSIZE(dropped); + SETHSIZE(recvdBelated); + SETHSIZE(undecrypted); +#undef SETHSIZE } void reset() diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 4dd235e06..f1ffceb29 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -1,10 +1,13 @@ #include "gtest/gtest.h" #include +#include #include #include "srt.h" +#include "sync.h" #include "netinet_any.h" using srt::sockaddr_any; +using namespace srt::sync; class TestIPv6 : public ::testing::Test @@ -36,6 +39,7 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); + m_CallerStarted = false; } void TearDown() @@ -48,7 +52,29 @@ class TestIPv6 } public: + + void SetupFileMode() + { + int val = SRTT_FILE; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + ASSERT_NE(srt_setsockflag(m_listener_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + } + + int m_CallerPayloadSize = 0; + int m_AcceptedPayloadSize = 0; + atomic m_CallerStarted; + + Condition m_ReadyToClose; + Mutex m_ReadyToCloseLock; + + // "default parameter" version. Can't use default parameters because this goes + // against binding parameters. Nor overloading. void ClientThread(int family, const std::string& address) + { + return ClientThreadFlex(family, address, true); + } + + void ClientThreadFlex(int family, const std::string& address, bool shouldwork) { sockaddr_any sa (family); sa.hport(m_listen_port); @@ -56,12 +82,38 @@ class TestIPv6 std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + + m_CallerStarted = true; + 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"); + if (shouldwork) + { + // Version with expected success + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + + if (connect_res == SRT_ERROR) + { + srt_close(m_listener_sock); + } + else + { + before_closing.wait(); + } + + PrintAddresses(m_caller_sock, "CALLER"); + } + else + { + // Version with expected failure + EXPECT_EQ(connect_res, SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); + srt_close(m_listener_sock); + } } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -81,6 +133,8 @@ class TestIPv6 if (accepted_sock == SRT_INVALID_SOCK) { return sockaddr_any(); } + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); PrintAddresses(accepted_sock, "ACCEPTED"); @@ -95,6 +149,9 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + before_closing.notify_one(); + srt_close(accepted_sock); return sn; } @@ -194,3 +251,82 @@ TEST_F(TestIPv6, v6_calls_v4) client.join(); } +TEST_F(TestIPv6, plsize_v6) +{ + SetupFileMode(); + + 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"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1444); // == 1500 - 32[IPv6] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1444); + + client.join(); +} + +TEST_F(TestIPv6, plsize_v4) +{ + SetupFileMode(); + + 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"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1456); // == 1500 - 20[IPv4] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1456); + + client.join(); +} + +TEST_F(TestIPv6, plsize_faux_v6) +{ + using namespace std::literals; + SetupFileMode(); + + 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); + + int oversize = 1450; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &oversize, sizeof (int)), -1); + + std::thread client(&TestIPv6::ClientThreadFlex, this, AF_INET6, "::1", false); + + // Set on sleeping to make sure that the thread started. + // Sleeping isn't reliable so do a dampened spinlock here. + // This flag also confirms that the caller acquired the mutex and will + // unlock it for CV waiting - so we can proceed to notifying it. + do std::this_thread::sleep_for(100ms); while (!m_CallerStarted); + + // Just in case of a test failure, kick CV to avoid deadlock + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + before_closing.notify_one(); + + client.join(); +} From 56fdfdb5f741423ff962ee48c4b5ea572be279ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 18:04:03 +0100 Subject: [PATCH 038/174] Withdrawn quotes for option names --- docs/API/API-socket-options.md | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2730dcc92..68e22d7c8 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -264,7 +264,7 @@ The following table lists SRT API socket options in alphabetical order. Option d ### Option Descriptions -#### `SRTO_BINDTODEVICE` +#### SRTO_BINDTODEVICE | OptName | Since | Restrict | Type | Units | Default | Range | Dir |Entity| | --------------------- | ----- | -------- | -------- | ------ | -------- | ------ |-----|------| @@ -287,7 +287,7 @@ for a process that runs as root. Otherwise the function that applies the setting --- -#### `SRTO_CONGESTION` +#### SRTO_CONGESTION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -309,7 +309,7 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST --- -#### `SRTO_CONNTIMEO` +#### SRTO_CONNTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -323,7 +323,7 @@ will be 10 times the value set with `SRTO_CONNTIMEO`. --- -#### `SRTO_CRYPTOMODE` +#### SRTO_CRYPTOMODE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | --------- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -377,7 +377,7 @@ There is no way to check the crypto mode being requested by the SRT caller at th --- -#### `SRTO_DRIFTTRACER` +#### SRTO_DRIFTTRACER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -389,7 +389,7 @@ Enables or disables time drift tracer (receiver). --- -#### `SRTO_ENFORCEDENCRYPTION` +#### SRTO_ENFORCEDENCRYPTION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -434,7 +434,7 @@ on the caller side. --- -#### `SRTO_EVENT` +#### SRTO_EVENT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -449,7 +449,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of --- -#### `SRTO_FC` +#### SRTO_FC | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -488,7 +488,7 @@ where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCV --- -#### `SRTO_GROUPCONNECT` +#### SRTO_GROUPCONNECT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -514,7 +514,7 @@ function will return the group, not this socket ID. --- -#### `SRTO_GROUPMINSTABLETIMEO` +#### SRTO_GROUPMINSTABLETIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -548,7 +548,7 @@ Note that the value of this option is not allowed to exceed the value of --- -#### `SRTO_GROUPTYPE` +#### SRTO_GROUPTYPE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -567,7 +567,7 @@ context than inside the listener callback handler, the value is undefined. --- -#### `SRTO_INPUTBW` +#### SRTO_INPUTBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -588,7 +588,7 @@ and keep the default 25% value for `SRTO_OHEADBW`*. --- -#### `SRTO_MININPUTBW` +#### SRTO_MININPUTBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -603,7 +603,7 @@ See [`SRTO_INPUTBW`](#SRTO_INPUTBW). --- -#### `SRTO_IPTOS` +#### SRTO_IPTOS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -621,7 +621,7 @@ and the actual value for connected sockets. --- -#### `SRTO_IPTTL` +#### SRTO_IPTTL | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -639,7 +639,7 @@ and the actual value for connected sockets. --- -#### `SRTO_IPV6ONLY` +#### SRTO_IPV6ONLY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -661,7 +661,7 @@ reliable way). Possible values are: --- -#### `SRTO_ISN` +#### SRTO_ISN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -678,7 +678,7 @@ used in any regular development.* --- -#### `SRTO_KMPREANNOUNCE` +#### SRTO_KMPREANNOUNCE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | @@ -712,7 +712,7 @@ The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. --- -#### `SRTO_KMREFRESHRATE` +#### SRTO_KMREFRESHRATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | @@ -734,7 +734,7 @@ might still be in flight, or packets that have to be retransmitted. --- -#### `SRTO_KMSTATE` +#### SRTO_KMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -752,7 +752,7 @@ for more details. --- -#### `SRTO_LATENCY` +#### SRTO_LATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -771,7 +771,7 @@ be sender and receiver at the same time, and `SRTO_SENDER` became redundant. --- -#### `SRTO_LINGER` +#### SRTO_LINGER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -789,7 +789,7 @@ The default value in [the file transfer configuration](./API.md#transmission-typ --- -#### `SRTO_LOSSMAXTTL` +#### SRTO_LOSSMAXTTL | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -810,7 +810,7 @@ By default this value is set to 0, which means that this mechanism is off. --- -#### `SRTO_MAXBW` +#### SRTO_MAXBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -834,7 +834,7 @@ therefore the default -1 remains even in live mode. --- -#### `SRTO_MESSAGEAPI` +#### SRTO_MESSAGEAPI | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -873,7 +873,7 @@ SCTP protocol. --- -#### `SRTO_MINVERSION` +#### SRTO_MINVERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -889,7 +889,7 @@ The default value is 0x010000 (SRT v1.0.0). --- -#### `SRTO_MSS` +#### SRTO_MSS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -973,7 +973,7 @@ the best approach is then to avoid it by using appropriate parameters. --- -#### `SRTO_NAKREPORT` +#### SRTO_NAKREPORT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -990,7 +990,7 @@ The default is true for Live mode, and false for File mode (see [`SRTO_TRANSTYPE --- -#### `SRTO_OHEADBW` +#### SRTO_OHEADBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1020,7 +1020,7 @@ and break quickly at any rise in packet loss. --- -#### `SRTO_PACKETFILTER` +#### SRTO_PACKETFILTER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1086,7 +1086,7 @@ For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-f --- -#### `SRTO_PASSPHRASE` +#### SRTO_PASSPHRASE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1115,7 +1115,7 @@ encrypted connection, they have to simply set the same passphrase. --- -#### `SRTO_PAYLOADSIZE` +#### SRTO_PAYLOADSIZE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1173,7 +1173,7 @@ For File mode: Default value is 0 and it's recommended not to be changed. --- -#### `SRTO_PBKEYLEN` +#### SRTO_PBKEYLEN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1260,7 +1260,7 @@ undefined behavior: --- -#### `SRTO_PEERIDLETIMEO` +#### SRTO_PEERIDLETIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1274,7 +1274,7 @@ considered broken on timeout. --- -#### `SRTO_PEERLATENCY` +#### SRTO_PEERLATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1295,7 +1295,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### `SRTO_PEERVERSION` +#### SRTO_PEERVERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1309,7 +1309,7 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. --- -#### `SRTO_RCVBUF` +#### SRTO_RCVBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1329,7 +1329,7 @@ than the Flight Flag size). --- -#### `SRTO_RCVDATA` +#### SRTO_RCVDATA | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1341,7 +1341,7 @@ Size of the available data in the receive buffer. --- -#### `SRTO_RCVKMSTATE` +#### SRTO_RCVKMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1355,7 +1355,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### `SRTO_RCVLATENCY` +#### SRTO_RCVLATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1397,7 +1397,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### `SRTO_RCVSYN` +#### SRTO_RCVSYN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1434,7 +1434,7 @@ derived from the socket of which the group is a member). --- -#### `SRTO_RCVTIMEO` +#### SRTO_RCVTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1448,7 +1448,7 @@ it will behave as if in "non-blocking mode". The -1 value means no time limit. --- -#### `SRTO_RENDEZVOUS` +#### SRTO_RENDEZVOUS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1461,7 +1461,7 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot --- -#### `SRTO_RETRANSMITALGO` +#### SRTO_RETRANSMITALGO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | @@ -1489,7 +1489,7 @@ Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). --- -#### `SRTO_REUSEADDR` +#### SRTO_REUSEADDR | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1516,7 +1516,7 @@ its address.* --- -#### `SRTO_SENDER` +#### SRTO_SENDER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1536,7 +1536,7 @@ parties simultaneously. --- -#### `SRTO_SNDBUF` +#### SRTO_SNDBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1548,7 +1548,7 @@ Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. --- -#### `SRTO_SNDDATA` +#### SRTO_SNDDATA | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1560,7 +1560,7 @@ Size of the unacknowledged data in send buffer. --- -#### `SRTO_SNDDROPDELAY` +#### SRTO_SNDDROPDELAY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1587,7 +1587,7 @@ always when requested). --- -#### `SRTO_SNDKMSTATE` +#### SRTO_SNDKMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1601,7 +1601,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### `SRTO_SNDSYN` +#### SRTO_SNDSYN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1627,7 +1627,7 @@ but will have no effect on the listener socket itself. --- -#### `SRTO_SNDTIMEO` +#### SRTO_SNDTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1641,7 +1641,7 @@ if in "non-blocking mode". The -1 value means no time limit. --- -#### `SRTO_STATE` +#### SRTO_STATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1653,7 +1653,7 @@ Returns the current socket state, same as `srt_getsockstate`. --- -#### `SRTO_STREAMID` +#### SRTO_STREAMID | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1682,7 +1682,7 @@ influence anything. --- -#### `SRTO_TLPKTDROP` +#### SRTO_TLPKTDROP | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1701,7 +1701,7 @@ enabled in sender if receiver supports it. --- -#### `SRTO_TRANSTYPE` +#### SRTO_TRANSTYPE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1717,7 +1717,7 @@ Values defined by enum `SRT_TRANSTYPE` (see above for possible values) --- -#### `SRTO_TSBPDMODE` +#### SRTO_TSBPDMODE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1735,7 +1735,7 @@ the application. --- -#### `SRTO_UDP_RCVBUF` +#### SRTO_UDP_RCVBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1748,7 +1748,7 @@ based on MSS value. Receive buffer must not be greater than FC size. --- -#### `SRTO_UDP_SNDBUF` +#### SRTO_UDP_SNDBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1761,7 +1761,7 @@ on `SRTO_MSS` value. --- -#### `SRTO_VERSION` +#### SRTO_VERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | From d7f32d067bb0d1e15a805a72d3060cf161956729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 18:07:04 +0100 Subject: [PATCH 039/174] Fixed links in the socket option table --- docs/API/API-socket-options.md | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 68e22d7c8..6a23aa349 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -201,66 +201,66 @@ 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_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | -| [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE) | 1.6.0-dev | pre | `int32_t` | | 0 (Auto) | [0, 2] | 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_GROUPMINSTABLETIMEO`](#SRTO_GROUPMINSTABLETIMEO) | 1.5.0 | pre | `int32_t` | ms | 60 | 60-... | W | GDI+ | -| [`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 | off \* | 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-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 | -| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..80] | W | 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_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` | | 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+ | -| [`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 | +| [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_CRYPTOMODE](#SRTO_CRYPTOMODE) | 1.6.0-dev | pre | `int32_t` | | 0 (Auto) | [0, 2] | 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_GROUPMINSTABLETIMEO](#SRTO_GROUPMINSTABLETIMEO) | 1.5.0 | pre | `int32_t` | ms | 60 | 60-... | W | GDI+ | +| [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 | off \* | 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-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 | +| [SRTO_PASSPHRASE](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..80] | W | 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_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` | | 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+ | +| [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 From 70de76348d31adb8bbe572eda1c2d7d24a35eea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 14:43:01 +0100 Subject: [PATCH 040/174] Fixed minimum MSS to 116. Fixed some other bux --- docs/API/API-socket-options.md | 4 ++-- srtcore/socketconfig.cpp | 8 +++++++- srtcore/stats.h | 8 ++++---- test/test_file_transmission.cpp | 6 ------ test/test_socket_options.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 6a23aa349..ef83cf220 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -227,7 +227,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [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-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [SRTO_MSS](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 116.. | 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 | @@ -893,7 +893,7 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 116.. | RW | GSD | Maximum Segment Size. This value represents the maximum size of a UDP packet sent by the system. Therefore the value of `SRTO_MSS` must not exceed the diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 27eda173b..8ea2e19bb 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -69,9 +69,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + using namespace srt_logging; const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::udpHeaderSize(AF_INET6) + CHandShake::m_iContentSize)) + const int handshake_size = CHandShake::m_iContentSize + (sizeof(uint32_t) * SRT_HS_E_SIZE); + const int minval = int(CPacket::udpHeaderSize(AF_INET6) + CPacket::HDR_SIZE + handshake_size); + if (ival < minval) + { + LOGC(kmlog.Error, log << "SRTO_MSS: minimum value allowed is " << minval << " = [IPv6][UDP][SRT] headers + minimum SRT handshake"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } co.iMSS = ival; diff --git a/srtcore/stats.h b/srtcore/stats.h index 14b0d131e..55d8d00d9 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -103,23 +103,23 @@ class BytesPackets: public BytesPacketsCount // Set IPv4-based header size value as a fallback. This will be fixed upon connection. BytesPackets() - : m_iPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) + : m_zPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) {} public: void setupHeaderSize(int size) { - m_iPacketHeaderSize = size; + m_zPacketHeaderSize = uint64_t(size); } uint64_t bytesWithHdr() const { - return m_bytes + m_packets * m_iPacketHeaderSize; + return m_bytes + m_packets * m_zPacketHeaderSize; } private: - int m_iPacketHeaderSize; + uint64_t m_zPacketHeaderSize; }; template diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 48d790471..50db5647a 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -226,8 +226,6 @@ TEST(FileTransmission, Setup46) int ipv4_and_ipv6 = 0; ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); - // srt_setloglevel(LOG_DEBUG); - ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); int connect_port = 5555; @@ -238,10 +236,6 @@ TEST(FileTransmission, Setup46) sa_lsn.sin_addr.s_addr = INADDR_ANY; sa_lsn.sin_port = htons(connect_port); - - srt_setloglevel(LOG_DEBUG); - - // Find unused a port not used by any other service. // Otherwise srt_connect may actually connect. int bind_res = -1; diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 9b6385344..f010d3aeb 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -193,7 +193,7 @@ const OptionTestEntry g_test_matrix_options[] = { 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_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 116, 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 From 2eb1159ae2116e82855951e7984df87db460d969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:21:41 +0100 Subject: [PATCH 041/174] Replaced rand_r with std c++ random --- test/test_file_transmission.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 50db5647a..de227cce3 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -268,10 +268,14 @@ TEST(FileTransmission, Setup46) const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 char buffer[SIZE]; - unsigned int randseed = std::time(NULL); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); for (size_t i = 0; i < SIZE; ++i) - buffer[i] = rand_r(&randseed); + { + buffer[i] = dis(mtrd); + } EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); From 9f48d3ca05cde662e5ebf0e0c067f14567f19ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:32:39 +0100 Subject: [PATCH 042/174] Fixed usage of C++14 literals in the test (build failures) --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index f1ffceb29..a889851e5 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -300,7 +300,7 @@ TEST_F(TestIPv6, plsize_v4) TEST_F(TestIPv6, plsize_faux_v6) { - using namespace std::literals; + using namespace std::chrono; SetupFileMode(); sockaddr_any sa (AF_INET6); @@ -322,7 +322,7 @@ TEST_F(TestIPv6, plsize_faux_v6) // Sleeping isn't reliable so do a dampened spinlock here. // This flag also confirms that the caller acquired the mutex and will // unlock it for CV waiting - so we can proceed to notifying it. - do std::this_thread::sleep_for(100ms); while (!m_CallerStarted); + do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); // Just in case of a test failure, kick CV to avoid deadlock CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); From edbb608a98400a10a456ebd00f160c34ee4b8940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:46:18 +0100 Subject: [PATCH 043/174] [MAINT] Upgraded CI: ubuntu to version 20.04 --- .github/workflows/android.yaml | 2 +- .github/workflows/cxx11-ubuntu.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 713c3f331..0af85fda3 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: NDK-R23 - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Setup Android NDK R23 diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 751b3fda1..500ff1beb 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: ubuntu - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 From 26a7be6e45b452e0f238d9b195c054a6bfc76302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 16:18:07 +0100 Subject: [PATCH 044/174] Fixed test logics (printing after closing) --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index a889851e5..e1c5844c2 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -96,6 +96,8 @@ class TestIPv6 int size = sizeof (int); EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + PrintAddresses(m_caller_sock, "CALLER"); + if (connect_res == SRT_ERROR) { srt_close(m_listener_sock); @@ -104,8 +106,6 @@ class TestIPv6 { before_closing.wait(); } - - PrintAddresses(m_caller_sock, "CALLER"); } else { From 3d387a2930784fa3fe95f46bf536cfd80c3a7925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 16:49:58 +0100 Subject: [PATCH 045/174] Attempted fix for a deadlock in test, added some tracking --- test/test_ipv6.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e1c5844c2..6498b6551 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -80,7 +80,7 @@ class TestIPv6 sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); @@ -100,10 +100,13 @@ class TestIPv6 if (connect_res == SRT_ERROR) { + std::cout << "Connect failed - [UNLOCK]\n"; + before_closing.locker().unlock(); // We don't need this lock here and it may deadlock srt_close(m_listener_sock); } else { + std::cout << "Connect succeeded, [UNLOCK-WAIT-CV]\n"; before_closing.wait(); } } @@ -149,6 +152,7 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + std::cout << "DoAccept: [LOCK-SIGNAL]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); before_closing.notify_one(); From 3eef5922a61cd50b8888f22f74bfeb3356008993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 17:16:50 +0100 Subject: [PATCH 046/174] Added expect and tracking to close socket in ReuseAddr test (Travis problem) --- test/test_reuseaddr.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index 60532eb80..3fb2168cd 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -404,14 +404,15 @@ void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_succes char pattern[4] = {1, 2, 3, 4}; - ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), + EXPECT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), 1316); EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << " LSN:@" << bindsock << " CLR:@" << g_client_sock << " ...\n"; - ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); - ASSERT_NE(srt_close(g_client_sock), SRT_ERROR); // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + EXPECT_NE(srt_close(accepted_sock), SRT_ERROR) << "ERROR: " << srt_getlasterror_str(); + // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + EXPECT_NE(srt_close(g_client_sock), SRT_ERROR) << "ERROR: " << srt_getlasterror_str(); std::cout << "[T/S] joining client async...\n"; launched.get(); From e2ab5f61b5a6c1159d4b2c85920dee5815e2a36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 18:16:47 +0100 Subject: [PATCH 047/174] Used relaxed signaling for the sake of Travis --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 6498b6551..472876ba8 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -153,8 +153,8 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - before_closing.notify_one(); + // XXX Deadlock here on Travis by unknown reason, hence do relaxed signaling. + CSync::notify_all_relaxed(m_ReadyToClose); srt_close(accepted_sock); return sn; From d081e507aa732e1b4f8221e99db6012ff161c740 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 12:23:02 +0100 Subject: [PATCH 048/174] Lock debug fix for tests --- test/test_ipv6.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 472876ba8..959ace41b 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -80,10 +80,12 @@ class TestIPv6 sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK]\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + std::cout << "[LOCKED] Connecting\n"; + m_CallerStarted = true; const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); @@ -106,8 +108,9 @@ class TestIPv6 } else { - std::cout << "Connect succeeded, [UNLOCK-WAIT-CV]\n"; + std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; before_closing.wait(); + std::cout << "Connect: [SIGNALED-LOCK]\n"; } } else @@ -117,6 +120,7 @@ class TestIPv6 EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); srt_close(m_listener_sock); } + std::cout << "Connect: [UNLOCKING...]\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -124,7 +128,10 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; - std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + // Printing may happen from different threads, avoid intelining. + std::ostringstream sout; + sout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + std::cout << sout.str(); } sockaddr_any DoAccept() @@ -153,8 +160,8 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - // XXX Deadlock here on Travis by unknown reason, hence do relaxed signaling. - CSync::notify_all_relaxed(m_ReadyToClose); + CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); + std::cout << "DoAccept: [UNLOCKED]\n"; srt_close(accepted_sock); return sn; @@ -329,8 +336,9 @@ TEST_F(TestIPv6, plsize_faux_v6) do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); // Just in case of a test failure, kick CV to avoid deadlock - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - before_closing.notify_one(); + std::cout << "TEST: [LOCK-SIGNAL]\n"; + CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); + std::cout << "TEST: [UNLOCKED]\n"; client.join(); } From ba5f9620118a5754b71a92a6e4ae29661cd7c203 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 12:53:03 +0100 Subject: [PATCH 049/174] Added timeout for lock-CV to avoid Travis problem --- test/test_ipv6.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 959ace41b..531f11526 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -109,8 +109,8 @@ class TestIPv6 else { std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; - before_closing.wait(); - std::cout << "Connect: [SIGNALED-LOCK]\n"; + bool signaled = before_closing.wait_for(seconds_from(10)); + std::cout << "Connect: [" << (signaled ? "SIGNALED" : "EXPIRED") << "-LOCK]\n"; } } else @@ -138,6 +138,8 @@ class TestIPv6 { sockaddr_any sc1; + using namespace std::chrono; + SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); if (accepted_sock == SRT_INVALID_SOCK) { @@ -160,8 +162,23 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); - std::cout << "DoAccept: [UNLOCKED]\n"; + + // Travis makes problems here by unknown reason. Try waiting up to 10s + // until it's possible, otherwise simply give up. The intention is to + // prevent from closing too soon before the caller thread has a chance + // to perform required verifications. After 10s we can consider it enough time. + + int nms = 10; + while (!m_ReadyToCloseLock.try_lock()) + { + this_thread::sleep_for(milliseconds_from(100)); + if (--nms == 0) + break; + } + + CSync::notify_all_relaxed(m_ReadyToClose); + m_ReadyToCloseLock.unlock(); + std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; srt_close(accepted_sock); return sn; From 320a79b8087442478bb268ad58565e72e14c6cd0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 14:07:40 +0100 Subject: [PATCH 050/174] Attempted more debug for test ipv6 for Travis --- test/test_ipv6.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 531f11526..70dfcd5dc 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -174,10 +174,14 @@ class TestIPv6 this_thread::sleep_for(milliseconds_from(100)); if (--nms == 0) break; + std::cout << "(lock failed, retrying " << nms << ")...\n"; } CSync::notify_all_relaxed(m_ReadyToClose); - m_ReadyToCloseLock.unlock(); + if (nms) + { + m_ReadyToCloseLock.unlock(); + } std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; srt_close(accepted_sock); From 1b3ccd78563935a69ffc8fc3d3f7863646470376 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 14:23:23 +0100 Subject: [PATCH 051/174] More debug for Travis --- test/test_ipv6.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 70dfcd5dc..e103c2a51 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -172,14 +172,16 @@ class TestIPv6 while (!m_ReadyToCloseLock.try_lock()) { this_thread::sleep_for(milliseconds_from(100)); + std::cout << "(lock failed, retrying " << nms << ")...\n"; if (--nms == 0) break; - std::cout << "(lock failed, retrying " << nms << ")...\n"; } + std::cout << "(signaling...)\n"; CSync::notify_all_relaxed(m_ReadyToClose); if (nms) { + std::cout << "(unlocking...)\n"; m_ReadyToCloseLock.unlock(); } std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; From 90b13b1175492454ec86f10c306d2409ac372da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 7 Mar 2023 16:45:51 +0100 Subject: [PATCH 052/174] Fixed test ipv6 to use promise-future for synchronization --- test/test_ipv6.cpp | 73 +++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e103c2a51..4a464f1c4 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "srt.h" #include "sync.h" #include "netinet_any.h" @@ -39,7 +40,11 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); - m_CallerStarted = false; + + m_CallerStarted.reset(new std::promise); + m_ReadyCaller.reset(new std::promise); + m_ReadyAccept.reset(new std::promise); + } void TearDown() @@ -62,10 +67,8 @@ class TestIPv6 int m_CallerPayloadSize = 0; int m_AcceptedPayloadSize = 0; - atomic m_CallerStarted; - Condition m_ReadyToClose; - Mutex m_ReadyToCloseLock; + std::unique_ptr> m_CallerStarted, m_ReadyCaller, m_ReadyAccept; // "default parameter" version. Can't use default parameters because this goes // against binding parameters. Nor overloading. @@ -76,17 +79,15 @@ class TestIPv6 void ClientThreadFlex(int family, const std::string& address, bool shouldwork) { + std::future ready_accepter = m_ReadyAccept->get_future(); + sockaddr_any sa (family); sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - - std::cout << "[LOCKED] Connecting\n"; - - m_CallerStarted = true; + m_CallerStarted->set_value(); const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); @@ -98,19 +99,19 @@ class TestIPv6 int size = sizeof (int); EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + m_ReadyCaller->set_value(); + PrintAddresses(m_caller_sock, "CALLER"); if (connect_res == SRT_ERROR) { std::cout << "Connect failed - [UNLOCK]\n"; - before_closing.locker().unlock(); // We don't need this lock here and it may deadlock srt_close(m_listener_sock); } else { - std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; - bool signaled = before_closing.wait_for(seconds_from(10)); - std::cout << "Connect: [" << (signaled ? "SIGNALED" : "EXPIRED") << "-LOCK]\n"; + std::cout << "Connect succeeded, [FUTURE-WAIT...]\n"; + ready_accepter.wait(); } } else @@ -120,7 +121,7 @@ class TestIPv6 EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); srt_close(m_listener_sock); } - std::cout << "Connect: [UNLOCKING...]\n"; + std::cout << "Connect: exit\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -138,21 +139,24 @@ class TestIPv6 { sockaddr_any sc1; - using namespace std::chrono; + // Make sure the caller started + m_CallerStarted->get_future().wait(); + std::cout << "DoAccept: caller started, proceeding to accept\n"; SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); if (accepted_sock == SRT_INVALID_SOCK) { return sockaddr_any(); } - int size = sizeof (int); - EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); - PrintAddresses(accepted_sock, "ACCEPTED"); sockaddr_any sn; EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); EXPECT_NE(sn.get_addr(), nullptr); + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); + + m_ReadyCaller->get_future().wait(); if (sn.get_addr() != nullptr) { @@ -161,30 +165,8 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } - std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - - // Travis makes problems here by unknown reason. Try waiting up to 10s - // until it's possible, otherwise simply give up. The intention is to - // prevent from closing too soon before the caller thread has a chance - // to perform required verifications. After 10s we can consider it enough time. - - int nms = 10; - while (!m_ReadyToCloseLock.try_lock()) - { - this_thread::sleep_for(milliseconds_from(100)); - std::cout << "(lock failed, retrying " << nms << ")...\n"; - if (--nms == 0) - break; - } - - std::cout << "(signaling...)\n"; - CSync::notify_all_relaxed(m_ReadyToClose); - if (nms) - { - std::cout << "(unlocking...)\n"; - m_ReadyToCloseLock.unlock(); - } - std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; + std::cout << "DoAccept: ready accept - promise SET\n"; + m_ReadyAccept->set_value(); srt_close(accepted_sock); return sn; @@ -356,12 +338,11 @@ TEST_F(TestIPv6, plsize_faux_v6) // Sleeping isn't reliable so do a dampened spinlock here. // This flag also confirms that the caller acquired the mutex and will // unlock it for CV waiting - so we can proceed to notifying it. - do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); + m_CallerStarted->get_future().wait(); // Just in case of a test failure, kick CV to avoid deadlock - std::cout << "TEST: [LOCK-SIGNAL]\n"; - CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); - std::cout << "TEST: [UNLOCKED]\n"; + std::cout << "TEST: [PROMISE-SET]\n"; + m_ReadyAccept->set_value(); client.join(); } From bc4059c708fa846b193440ab3a844ce47e4c09c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 7 Mar 2023 17:11:17 +0100 Subject: [PATCH 053/174] Fixed filtering-out IPv6 tests for Travis --- .travis.yml | 2 +- test/test_ipv6.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eaca3a571..26c6eaa41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,7 +71,7 @@ matrix: - BUILD_TYPE=Release - BUILD_OPTS='-DENABLE_MONOTONIC_CLOCK=ON' script: - - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*:ReuseAddr.ProtocolVersion:ReuseAddr.*6" ; # Tests to skip due to lack of IPv6 support + - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.*6:ReuseAddr.ProtocolVersion:ReuseAddr.*6" ; # 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++"; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 4a464f1c4..7c9086c88 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -211,7 +211,7 @@ TEST_F(TestIPv6, v4_calls_v6_mapped) client.join(); } -TEST_F(TestIPv6, v6_calls_v6_mapped) +TEST_F(TestIPv6, v6_calls_mapped_v6) { sockaddr_any sa (AF_INET6); sa.hport(m_listen_port); From 11574a03d4446ba11e3cc72cd168435080c03d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 9 Mar 2023 11:04:43 +0100 Subject: [PATCH 054/174] Fixed wrong comment --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c66492375..9d63586bf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5619,8 +5619,8 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); // IMPORTANT: // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent - // over the current connection - which means that if any party is IPv6, then the maximum size - // is the one for IPv6 (1444). Only if both parties are IPv4, this maximum size is 1456. + // over the current connection - which means that if both parties are IPv6, then the maximum size + // is the one for IPv6 (1444). If any party is IPv4, this maximum size is 1456. // The family as the first argument is something different - it's for the header size in order // to calculate rate and statistics. From 88b9f33ffef7da24b1866c4c8b20d5f0c3130a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 10 May 2023 17:27:26 +0200 Subject: [PATCH 055/174] Fixed build break on Android --- srtcore/srt_c_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 21c5995ce..2e25acd08 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -126,7 +126,7 @@ SRTSOCKET srt_connect_bind(SRTSOCKET u, SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { - if (CUDT::isgroup(u)) + if (isgroup(u)) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); bool yes = 1; From 397dc7c08fe3eb21580a453483b4f1e5e5086d2e Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Fri, 16 Jun 2023 00:15:07 +0800 Subject: [PATCH 056/174] [core] Fixed overrideSndSeqNo() not clear buffer. --- srtcore/buffer_snd.cpp | 12 ++++++++++++ srtcore/buffer_snd.h | 1 + srtcore/core.cpp | 16 ++++++++++++---- srtcore/group.cpp | 27 +++++++++++++++------------ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 26f885dd6..1fc90eb40 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -663,6 +663,18 @@ int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_ return (dpkts); } +int CSndBuffer::dropAll(int& w_bytes) +{ + ScopedLock bufferguard(m_BufLock); + const int dpkts = m_iCount; + w_bytes = m_iBytesCount; + m_pFirstBlock = m_pCurrBlock = m_pLastBlock; + m_iCount = 0; + m_iBytesCount = 0; + updAvgBufSize(steady_clock::now()); + return dpkts; +} + void CSndBuffer::increase() { int unitsize = m_pBuffer->m_iSize; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 4440b9bfd..13446258b 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -158,6 +158,7 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + int dropAll(int& bytes); void updAvgBufSize(const time_point& time); int getAvgBufSize(int& bytes, int& timespan); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0e3cce0ee..df30b7fc9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9784,7 +9784,15 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return false; } - // + int dbytes; + const int dpkts SRT_ATR_UNUSED = m_pSndBuffer->dropAll((dbytes)); + + enterCS(m_StatsLock); + m_stats.sndr.dropped.count(dbytes);; + leaveCS(m_StatsLock); + + m_pSndLossList->removeUpTo(CSeqNo::decseq(seq)); + // The peer will have to do the same, as a reaction on perceived // packet loss. When it recognizes that this initial screwing up // has happened, it should simply ignore the loss and go on. @@ -9796,9 +9804,9 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) // the latter is ahead with the number of packets already scheduled, but // not yet sent. - HLOGC(gslog.Debug, log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo - << " (unchanged)" - ); + HLOGC(gslog.Debug, + log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo + << " dropped-pkts=" << dpkts); return true; } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f4dfba1ba..3986aa1d5 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3798,7 +3798,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // This should resend all packets if (m_SenderBuffer.empty()) { - LOGC(gslog.Fatal, log << "IPE: sendBackupRexmit: sender buffer empty"); + LOGC(gslog.Fatal, log << core.CONID() << "IPE: sendBackupRexmit: sender buffer empty"); // Although act as if it was successful, otherwise you'll get connection break return 0; @@ -3830,8 +3830,9 @@ 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. Link seqno %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq - << " - DENIED; skip " << skip_initial << " pkts, " << m_SenderBuffer.size() << " pkts in buffer"); + log << core.CONID() << "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 { @@ -3840,11 +3841,11 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // sequence with it first so that they go hand-in-hand with // 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 SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); - HLOGC(gslog.Debug, - log << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq - << (su ? " - succeeded" : " - FAILED!")); + const int32_t old SRT_ATR_UNUSED = core.schedSeqNo(); + const bool success SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); + LOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq + << (success ? " - succeeded" : " - FAILED!")); } } @@ -3852,8 +3853,8 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) 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"); + log << core.CONID() << "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 } @@ -3869,14 +3870,16 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { // Stop sending if one sending ended up with error LOGC(gslog.Warn, - log << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() << " and FAILED"); + log << core.CONID() << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() + << " and FAILED"); return -1; } } // Copy the contents of the last item being updated. w_mc = m_SenderBuffer.back().mc; - HLOGC(gslog.Debug, log << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); + HLOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); return stat; } From 76581458b1def63b6f89a169bfdf8c6aa7b2cfff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 11 Sep 2023 09:44:24 +0200 Subject: [PATCH 057/174] Fixed a bug introduced during upstream merge --- srtcore/core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eff90c51d..a4fe7559c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5678,6 +5678,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") << " authtag=" << authtag); + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iPeerISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space. From 41a86bfc4252d67e560e4983265c0ee1802c9a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 11 Sep 2023 10:13:25 +0200 Subject: [PATCH 058/174] Fixed tests that should require IPv6 enabled --- docs/API/API-socket-options.md | 36 ++++++++++++++++++++-------------- test/test_ipv6.cpp | 4 ++++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index f6ca5caad..ec2f5400d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -940,19 +940,25 @@ This value is a sum of: For the default 1500 the "remaining space" part is effectively 1456 for IPv4 and 1444 for IPv6. -Note that the IP version used here is not the domain of the socket, but the -in-transmission IP version. This is IPv4 for a case when the current socket's -binding address is of IPv4 domain, or if it is IPv6, but the peer's address -is then an IPv6-mapped-IPv4 address. The in-transmission IPv6 is only if the -peer's address is a true IPv6 address. Hence it is not possible to deteremine -all these limitations until the connection is established. Parts of SRT that -must allocate any resources regarding this value prior to connecting are using -the layout as per IPv4 because this results in a greater available space for -the "remaining space". +Note that the IP version used here is not the domain of the underlying UDP +socket, but the in-transmission IP version. This is effectively IPv4 in the +following cases: + +* when the current socket's binding address is of IPv4 domain +* when the peer's address is an IPv6-mapped-IPv4 address + +The IPv6 in-transmission IP version is assumed only if the peer's address +is a true IPv6 address (not IPv4 mapped). It is then not possible to determine +what the payload size limit until the connection is established. Parts of the +SRT facilities that must allocate any resources according to this value prior +to connecting are using the layout as per IPv4 because this way they allocate +more space than needed in the worst case. This value can be set on both connection parties independently, but after connection this option gets an effectively negotiated value, which is the less -one from both parties. +one from both parties. If this effective value is too small on any of the +connection peers, the connection is rejected (or late-rejected on the caller +side). This value then effectively controls: @@ -987,12 +993,12 @@ over the link used for the connection). See also limitations for In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit for one single packet sending, and therefore bigger portions of data are internally split into smaller portions, each one using the maximum available -"remaining space". The best value for this case is then equal to the current -network device's MTU size. Setting a greater value is possible (maximum for the -system API is 65535), but it may lead to packet fragmentation on the system -level. This is highly unwanted in SRT because: +"remaining space". The best value of `SRTO_MSS` for this case is then equal to +the current network device's MTU size. Setting a greater value is possible +(maximum for the system API is 65535), but it may lead to packet fragmentation +on the system level. This is highly unwanted in SRT because: -* SRT does also its own fragmentation, so it would be counter-productive +* Here SRT does also its own fragmentation, so it would be counter-productive * It would use more system resources with no advantage * SRT is unaware of it, so the statistics will be slightly falsified diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index afc65e894..e8c14141d 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -272,6 +272,8 @@ TEST_F(TestIPv6, v6_calls_v4) TEST_F(TestIPv6, plsize_v6) { + SRTST_REQUIRES(IPv6); + SetupFileMode(); sockaddr_any sa (AF_INET6); @@ -319,6 +321,8 @@ TEST_F(TestIPv6, plsize_v4) TEST_F(TestIPv6, plsize_faux_v6) { + SRTST_REQUIRES(IPv6); + using namespace std::chrono; SetupFileMode(); From 45809bdbbdf3d979a3ecd7a4939653fb8a389554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 18 Sep 2023 13:08:01 +0200 Subject: [PATCH 059/174] Pre-refax of change-independent parts for 2677 --- srtcore/buffer_rcv.cpp | 9 +++++++-- srtcore/channel.cpp | 9 +++++++++ srtcore/common.cpp | 2 +- srtcore/common.h | 9 +++++++++ srtcore/congctl.cpp | 5 ++++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 1f46788aa..7ea0945bc 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -130,7 +130,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) - , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) + , m_uAvgPayloadSz(0) { SRT_ASSERT(size < size_t(std::numeric_limits::max())); // All position pointers are integers } @@ -758,7 +758,12 @@ void CRcvBuffer::countBytes(int pkts, int bytes) 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); + { + if (!m_uAvgPayloadSz) + m_uAvgPayloadSz = bytes; + else + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + } } void CRcvBuffer::releaseUnitInPos(int pos) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 091adf115..38ce5b598 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1016,6 +1016,15 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet if ((msg_flags & errmsgflg[i].first) != 0) flg << " " << errmsgflg[i].second; + if (msg_flags & MSG_TRUNC) + { + // Additionally show buffer information in this case + flg << " buffers: "; + for (size_t i = 0; i < CPacket::PV_SIZE; ++i) + { + flg << "[" << w_packet.m_PacketVector[i].iov_len << "] "; + } + } // This doesn't work the same way on Windows, so on Windows just skip it. #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index b621c8025..dc60b3db7 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -205,7 +205,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc { // Check if the peer address is a model of IPv4-mapped-on-IPv6. // If so, it means that the `ip` array should be interpreted as IPv4. - const bool is_mapped_ipv4 = checkMappedIPv4((uint16_t*)peer.sin6.sin6_addr.s6_addr); + const bool is_mapped_ipv4 = checkMappedIPv4(peer.sin6); sockaddr_in6* a = (&w_addr.sin6); diff --git a/srtcore/common.h b/srtcore/common.h index 5021fa5a8..de42a7d83 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1422,6 +1422,15 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(const std::string& s, SrtConfig& w_config); +bool checkMappedIPv4(const uint16_t* sa); + +inline bool checkMappedIPv4(const sockaddr_in6& sa) +{ + const uint16_t* addr = reinterpret_cast(&sa.sin6_addr.s6_addr); + return checkMappedIPv4(addr); +} + + } // namespace srt #endif diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 91c73d660..b9265c046 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -63,6 +63,7 @@ class LiveCC: public SrtCongestionControlBase int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; + size_t m_zHeaderSize; // NAKREPORT stuff. int m_iMinNakInterval_us; // Minimum NAK Report Period (usec) @@ -81,6 +82,8 @@ class LiveCC: public SrtCongestionControlBase m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; + m_zHeaderSize = parent->m_config.iMSS - parent->maxPayloadSize(); + m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) @@ -173,7 +176,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod << " by avg pktsize=" << m_zSndAvgPayloadSize From 0c3abe051623e70ff0d2c51e383e48b745d3d657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 18 Sep 2023 15:15:51 +0200 Subject: [PATCH 060/174] A fix from code review --- srtcore/api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 8ce383d84..5646e3e79 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3160,7 +3160,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons // We can't use maxPayloadSize() because this value isn't valid until the connection is established. // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. - size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + const size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 << " plsize=" << payload_size << " hsize=" << 1024); From ce2a043e86120b734d3ea98a13fe93d528700e29 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 08:38:11 +0200 Subject: [PATCH 061/174] Apply SOME suggestions from the doc review (others pending) Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 62 ++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index ec2f5400d..cb6b88f2e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -947,20 +947,19 @@ following cases: * when the current socket's binding address is of IPv4 domain * when the peer's address is an IPv6-mapped-IPv4 address -The IPv6 in-transmission IP version is assumed only if the peer's address -is a true IPv6 address (not IPv4 mapped). It is then not possible to determine -what the payload size limit until the connection is established. Parts of the -SRT facilities that must allocate any resources according to this value prior -to connecting are using the layout as per IPv4 because this way they allocate -more space than needed in the worst case. +The IPv6 transmission case is assumed only if the peer's address is a true IPv6 address +(not IPv4 mapped). It is then not possible to determine the payload size limit +until the connection is established. SRT operations that must allocate any +resources according to this value prior to connecting will assume IPv4 transmission +because this way, in the worst case, they allocate more space than needed . This value can be set on both connection parties independently, but after -connection this option gets an effectively negotiated value, which is the less -one from both parties. If this effective value is too small on any of the +connection `SRTO_MSS` gets a negotiated value, which is the lesser +of the two. If this effective value is too small for either of the connection peers, the connection is rejected (or late-rejected on the caller side). -This value then effectively controls: +This value then controls: * The maximum size of the payload in a single UDP packet ("remaining space"). @@ -970,24 +969,23 @@ in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it is that some buffer resources are allocated prior to the connection, so this value must fit both IPv4 and IPv6 for buffer memory allocation. -The default value 1500 matches the standard MTU size for network devices. It -is recommended that this value be set at maximum to the value of MTU size of -the network device that you will use for connection. - -Detailed recommendations for this value differ in the file and live mode. - -In the live mode a single call to `srt_send*` function may only send data -that fit in one packet. This size is defined by the `SRTO_PAYLOADSIZE` -option (defult: 1316) and it is also the size of the data in a single UDP -packet. To save memory space, you may want then to set MSS in live mode to -a value for which the "remaining space" matches `SRTO_PAYLOADSIZE` value (for -default 1316 it will be 1360 for IPv4 and 1372 for IPv6). This is not done by -default for security reasons: this may potentially lead to inability to read an -incoming UDP packet if its size is by some reason bigger than the negotiated MSS. -This may lead to misleading situations and hard to detect errors. You should -set such a value only if the peer is trusted (that is, you can be certain that -it will never come to a situation of having received an oversized UDP packet -over the link used for the connection). See also limitations for +The default value of 1500 corresponds to the standard MTU size for network devices. It +is recommended that this value be set to the maximum MTU size of +the network device that you will use for the connection. + +The recommendations for the value of `SRTO_MSS` differ between file and live modes. + +In live mode a single call to the `srt_send*` function may only send data +that fits in one packet. This size is defined by the `SRTO_PAYLOADSIZE` +option (defult: 1316), and it is also the size of the data in a single UDP +packet. To save memory space, you may want then to set `SRTO_MSS` in live mode to +a value for which the "remaining space" matches the `SRTO_PAYLOADSIZE` value (for +the default value of 1316 this will be 1360 for IPv4 and 1372 for IPv6). For security reasons, +this is not done by default: it may potentially lead to the inability to read an incoming UDP +packet if its size is for some reason bigger than the negotiated MSS, which may in turn lead +to unpredictable behaviour and hard-to-detect errors. You should set such a value only if +the peer is trusted (that is, you can be certain that you will never receive an oversized UDP +packet over the link used for the connection). You should also consider the limitations of `SRTO_PAYLOADSIZE`. In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit @@ -998,12 +996,12 @@ the current network device's MTU size. Setting a greater value is possible (maximum for the system API is 65535), but it may lead to packet fragmentation on the system level. This is highly unwanted in SRT because: -* Here SRT does also its own fragmentation, so it would be counter-productive -* It would use more system resources with no advantage -* SRT is unaware of it, so the statistics will be slightly falsified +* SRT also performs its own fragmentation, so it would be counter-productive +* It would use more system resources to no advantage +* SRT is unaware of it, so the resulting statistics would be slightly misleading -The system-level packet fragmentation cannot be however reliably turned off; -the best approach is then to avoid it by using appropriate parameters. +System-level packet fragmentation cannot be reliably turned off, +so safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) From 4bb7b47998c5e1c82b8649b95ba2150e5bd073e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 10:18:04 +0200 Subject: [PATCH 062/174] Added mutex spec to a function --- srtcore/core.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/core.h b/srtcore/core.h index 6ae9c7e89..538df167c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -668,6 +668,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_EXCLUDES(m_RcvBufferLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) From d1798784a1e6573461a9caa493603eba63ba2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 13:05:27 +0200 Subject: [PATCH 063/174] Added more thread check entries --- srtcore/api.h | 2 ++ srtcore/core.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..9a5c6081c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,6 +123,8 @@ class CUDTSocket void construct(); + // XXX Controversial as to whether it should be guarded by this lock. + // It is used in many places without the lock, and it is also atomic. SRT_ATTR_GUARDED_BY(m_ControlLock) sync::atomic m_Status; //< current socket state diff --git a/srtcore/core.h b/srtcore/core.h index 538df167c..b8a24032a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -318,6 +318,7 @@ class CUDT #endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + SRT_ATTR_REQUIRES(m_RecvAckLock) int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } @@ -365,6 +366,7 @@ class CUDT /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @returns The number of packets in flight belonging to the interval [0; ...) + SRT_ATTR_REQUIRES(m_RecvAckLock) int32_t getFlightSpan() const { return getFlightSpan(m_iSndLastAck, m_iSndCurrSeqNo); From 5c2c876b530a25b5c80f0d70306bea54d9416045 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:50:50 +0200 Subject: [PATCH 064/174] Apply suggestions from doc review (still pending) Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index cb6b88f2e..21da9c2a3 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -927,17 +927,17 @@ The default value is 0x010000 (SRT v1.0.0). Maximum Segment Size. This value represents the maximum size of a UDP packet sent by the system. Therefore the value of `SRTO_MSS` must not exceed the values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer -allocation and rate calculation using packet counter assuming fully filled +allocation and rate calculation using a packet counter that assumes fully filled packets. This value is a sum of: -* IP header (20 bytes for IPv4 or 32 bytes for IPv6) +* IP header (20 bytes for IPv4, or 32 bytes for IPv6) * UDP header (8 bytes) * SRT header (16 bytes) * remaining space (as the maximum payload size available for a packet) -For the default 1500 the "remaining space" part is effectively 1456 for IPv4 +For the default 1500 the "remaining space" is effectively 1456 for IPv4 and 1444 for IPv6. Note that the IP version used here is not the domain of the underlying UDP @@ -951,7 +951,7 @@ The IPv6 transmission case is assumed only if the peer's address is a true IPv6 (not IPv4 mapped). It is then not possible to determine the payload size limit until the connection is established. SRT operations that must allocate any resources according to this value prior to connecting will assume IPv4 transmission -because this way, in the worst case, they allocate more space than needed . +because this way, in the worst case, they allocate more space than needed. This value can be set on both connection parties independently, but after connection `SRTO_MSS` gets a negotiated value, which is the lesser @@ -988,8 +988,8 @@ the peer is trusted (that is, you can be certain that you will never receive an packet over the link used for the connection). You should also consider the limitations of `SRTO_PAYLOADSIZE`. -In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit -for one single packet sending, and therefore bigger portions of data are +In file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for sending a single packet, and therefore bigger portions of data are internally split into smaller portions, each one using the maximum available "remaining space". The best value of `SRTO_MSS` for this case is then equal to the current network device's MTU size. Setting a greater value is possible From 0403820d6dbb1073dec6e0282d805526a0d7c838 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:51:30 +0200 Subject: [PATCH 065/174] Update doc review (still pending) --- docs/API/API-socket-options.md | 55 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 21da9c2a3..f4581c358 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1155,45 +1155,52 @@ encrypted connection, they have to simply set the same passphrase. | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | -Sets the data limitation mode and the maximum data size for sending at once. +Sets the mode that determines the limitations on how data is sent, including the maximum +size of payload data sent within a single UDP packet. This option can be only set prior +to connecting, but it can be read also after the connection has been established. -The default value is 0 in the file mode and 1316 in live mode (this is one of -the options modified together with `SRTO_TRANSTYPE`). +The default value is 1316 in live mode (which is default) and 0 in file mode (when file +mode is set through the `SRTO_TRANSTYPE` option). -If the value is 0, this means a "file mode", in which the call to `srt_send*` -is not limited to a size fitting in one single packet, that is, the supplied -data will be split into multiple pieces fitting in a single UDP packet, if -necessary, as well as every packet will use the maximum space available -in a UDP packet (except the last in the stream or in the message) according to -the `SRTO_MSS` setting and others that may influence this size (such as -`SRTO_PACKETFILTER` and `SRTO_CRYPTOMODE`). +In file mode (`SRTO_PAYLOADSIZE` = 0) the call to `srt_send*` is not limited to the size +of a single packet. If necessary, the supplied data will be split into multiple pieces, +each fitting into a single UDP packet. Every data payload (except the last one in the +stream or in the message) will use the maximum space available in a UDP packet, +as determined by `SRTO_MSS` and other settings that may influence this size +(such as [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) and +[`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)). -If the value is greater than 0, this means a "live mode", and the value -defines the maximum size of: +Also when this option is set to 0 prior to connecting, then reading this option +from a connected socket will return the maximum size of the payload that fits +in a single packet according to the current connection parameters. -* the single call to a sending function (`srt_send*`) -* the payload supplied in every single data packet +In live mode (`SRTO_PAYLOADSIZE` > 0) the value defines the maximum size of: + +* a single call to a sending function (`srt_send*`) +* the payload supplied in each data packet + +as well as the minimum size of the buffer used for the `srt_recv*` call. This value can be set to a greater value than the default 1316, but the maximum possible value is limited by the following factors: -* 1500 is the default MSS (see `SRTO_MSS`), including headers, which are: - * 20 bytes for IPv4 or 32 bytes for IPv6 +* 1500 bytes is the default MSS (see [`SRTO_MSS`](#SRTO_MSS)), including headers, which occupy: + * 20 bytes for IPv4, or 32 bytes for IPv6 * 8 bytes for UDP * 16 bytes for SRT -This alone gives the limit of 1456 for IPv4 and 1444 for IPv6. This limit may -be however further decreased in the following cases: +This alone gives a limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be further decreased in the following cases: -* 4 bytes reserved for FEC, if you use the builtin FEC packet filter (see `SRTO_PACKETFILTER`) -* 16 bytes reserved for the authentication tag, if you use AES GCM (see `SRTO_CRYPTOMODE`) +* 4 bytes reserved for FEC, if you use the built in FEC packet filter (see [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER)) +* 16 bytes reserved for the authentication tag, if you use AES GCM (see [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)) -**WARNING**: The option setter will reject the setting if this value is too -great, but note that not every limitation can be checked prior to connection. +**WARNING**: The party setting the options will reject a value that is too +large, but note that not every limitation can be checked prior to connection. This includes: -* MSS defined by the peer, which may override MSS set in the agent -* The in-transmission IP version - see `SRTO_MSS` for details +* the MSS value defined by a peer, which may override the MSS set by an agent +* the in-transmission IP version (see [SRTO_MSS](#SRTO_MSS) for details) These values also influence the "remaining space" in the packet to be used for payload. If during the handshake it turns out that this "remaining space" is From e02d85a886ad2ee1d4f093d6a259af828fc23ae2 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:52:25 +0200 Subject: [PATCH 066/174] Apply suggestions from doc review (complete) Co-authored-by: stevomatthews --- 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 f4581c358..4c0c9640b 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1001,7 +1001,7 @@ on the system level. This is highly unwanted in SRT because: * SRT is unaware of it, so the resulting statistics would be slightly misleading System-level packet fragmentation cannot be reliably turned off, -so safest approach is to avoid it by using appropriate parameters. +so the safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) From 6074f219ef53432c8a5ff9c4d6242116a2e848e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 2 Oct 2023 11:25:05 +0200 Subject: [PATCH 067/174] Added extra v6-to-v6 test to check correct payload size --- srtcore/core.cpp | 2 +- test/test_file_transmission.cpp | 225 ++++++++++++++++++++++---------- 2 files changed, 157 insertions(+), 70 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 99e6df610..e3c29b651 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5670,7 +5670,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) // The family as the first argument is something different - it's for the header size in order // to calculate rate and statistics. - int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(m_TransferIPVersion); SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index a81fb301b..d28532cc0 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -201,11 +201,9 @@ TEST(FileTransmission, Upload) TEST(FileTransmission, Setup46) { - SRTST_REQUIRES(IPv6); - using namespace srt; - - srt_startup(); + SRTST_REQUIRES(IPv6); + TestInit srtinit; SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); @@ -213,98 +211,187 @@ TEST(FileTransmission, Setup46) srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); - try - { - // Setup a connection with IPv6 caller and IPv4 listener, - // then send data of 1456 size and make sure two packets were used. + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. - // So first configure a caller for IPv6 socket, capable of - // using IPv4. As the IP version isn't specified now when - // creating a socket, force binding explicitly. + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. - // This creates the "any" spec for IPv6 with port = 0 - sockaddr_any sa(AF_INET6); + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); - int ipv4_and_ipv6 = 0; - ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); - ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); - int connect_port = 5555; + int connect_port = 5555; - // Configure listener - sockaddr_in sa_lsn = sockaddr_in(); - sa_lsn.sin_family = AF_INET; - sa_lsn.sin_addr.s_addr = INADDR_ANY; - sa_lsn.sin_port = htons(connect_port); + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); - // Find unused a port not used by any other service. - // Otherwise srt_connect may actually connect. - int bind_res = -1; - for (connect_port = 5000; connect_port <= 5555; ++connect_port) + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) { - sa_lsn.sin_port = htons(connect_port); - bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); - if (bind_res == 0) - { - std::cout << "Running test on port " << connect_port << "\n"; - break; - } - - ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + std::cout << "Running test on port " << connect_port << "\n"; + break; } - ASSERT_GE(bind_res, 0); + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); - srt_listen(sock_lsn, 1); + srt_listen(sock_lsn, 1); - ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); - sa.hport(connect_port); + sa.hport(connect_port); - ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); - int sock_acp = -1; - ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); - const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 - char buffer[SIZE]; + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; - std::random_device rd; - std::mt19937 mtrd(rd()); - std::uniform_int_distribution dis(0, UINT8_MAX); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + +} - for (size_t i = 0; i < SIZE; ++i) +TEST(FileTransmission, Setup66) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + // Require that the connection allows both IP versions. + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + ASSERT_NE(srt_setsockflag(sock_lsn, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_any sa_lsn(AF_INET6); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.hport(connect_port); + bind_res = srt_bind(sock_lsn, sa_lsn.get(), sa_lsn.size()); + if (bind_res == 0) { - buffer[i] = dis(mtrd); + std::cout << "Running test on port " << connect_port << "\n"; + break; } - EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); - char resultbuf[SIZE]; - EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + srt_listen(sock_lsn, 1); - // It should use the maximum payload size per packet reported from the option. - int payloadsize_back = 0; - int payloadsize_back_size = sizeof (payloadsize_back); - EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); - EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + ASSERT_EQ(inet_pton(AF_INET6, "::1", &sa.sin6.sin6_addr), 1); - SRT_TRACEBSTATS snd_stats, rcv_stats; - srt_bstats(sock_acp, &snd_stats, 0); - srt_bstats(sock_clr, &rcv_stats, 0); + sa.hport(connect_port); - EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); - EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + std::cout << "Connecting to: " << sa.str() << std::endl; - } - catch (...) + int connect_result = srt_connect(sock_clr, sa.get(), sa.size()); + ASSERT_EQ(connect_result, 0) << srt_getlasterror_str(); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), SRT_ERROR); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) { - srt_cleanup(); - throw; + buffer[i] = dis(mtrd); } - srt_cleanup(); -} + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); -// XXX Setup66 - establish an IPv6 to IPv6 connection and make sure max payload size is that of IPv6. + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET6); + std::cout << "Payload size: " << payloadsize_back << std::endl; + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + // We use the same data size that fit in 1 payload IPv4, but not IPv6. + // Therefore sending should be here split into two packets. + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 2); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 2); + +} From 920969bbf15189173948f1e0b11db3ba775b277d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 10 Oct 2023 14:35:45 +0200 Subject: [PATCH 068/174] Synchronized and improved --- apps/srt-file-transmit.cpp | 14 +-- apps/srt-live-transmit.cpp | 14 +-- apps/srt-tunnel.cpp | 22 ++-- common/devel_util.h | 23 +++- srtcore/api.cpp | 2 +- srtcore/core.h | 2 +- srtcore/group.h | 2 +- srtcore/srt.h | 13 +- srtcore/srt_c_api.cpp | 2 +- test/test_enforced_encryption.cpp | 192 +++++++++++++++--------------- test/test_epoll.cpp | 160 ++++++++++++------------- test/test_fec_rebuilding.cpp | 14 +-- test/test_file_transmission.cpp | 9 +- test/test_ipv6.cpp | 4 +- testing/srt-test-file.cpp | 6 +- testing/srt-test-live.cpp | 2 +- 16 files changed, 254 insertions(+), 227 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..2bd22a019 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -309,7 +309,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -349,7 +349,7 @@ bool DoUpload(UriParser& ut, string path, string filename, s = tar->GetSRTSocket(); int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -391,7 +391,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int st = tar->Write(buf.data() + shift, n, 0, out_stats); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; @@ -429,7 +429,7 @@ bool DoUpload(UriParser& ut, string path, string filename, size_t bytes; size_t blocks; int st = srt_getsndbuffer(s, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; @@ -490,7 +490,7 @@ bool DoDownload(UriParser& us, string directory, string filename, int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -528,7 +528,7 @@ bool DoDownload(UriParser& us, string directory, string filename, s = src->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -593,7 +593,7 @@ bool DoDownload(UriParser& us, string directory, string filename, } int n = src->Read(cfg.chunk_size, packet, out_stats); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; goto exit; diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 24ed33be7..e608034f9 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -518,7 +518,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -527,7 +527,7 @@ int main(int argc, char** argv) break; case UriParser::UDP: if (srt_epoll_add_ssock(pollid, - src->GetSysSocket(), &events)) + src->GetSysSocket(), &events) == SRT_ERROR) { cerr << "Failed to add UDP source to poll, " << src->GetSysSocket() << endl; @@ -536,7 +536,7 @@ int main(int argc, char** argv) break; case UriParser::FILE: if (srt_epoll_add_ssock(pollid, - src->GetSysSocket(), &events)) + src->GetSysSocket(), &events) == SRT_ERROR) { cerr << "Failed to add FILE source to poll, " << src->GetSysSocket() << endl; @@ -566,7 +566,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -639,7 +639,7 @@ int main(int argc, char** argv) SRTSOCKET ns = (issource) ? src->GetSRTSocket() : tar->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, ns, &events)) + if (srt_epoll_add_usock(pollid, ns, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll, " << ns << endl; @@ -737,7 +737,7 @@ int main(int argc, char** argv) const int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; // Disable OUT event polling when connected if (srt_epoll_update_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -781,7 +781,7 @@ int main(int argc, char** argv) std::shared_ptr pkt(new MediaPacket(transmit_chunk_size)); const int res = src->Read(transmit_chunk_size, *pkt, out_stats); - if (res == SRT_ERROR && src->uri.type() == UriParser::SRT) + if (res == int(SRT_ERROR) && src->uri.type() == UriParser::SRT) { if (srt_getlasterror(NULL) == SRT_EASYNCRCV) break; diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 569a0c26e..8f4a6b7db 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -419,7 +419,7 @@ void Engine::Worker() class SrtMedium: public Medium { - SRTSOCKET m_socket = SRT_ERROR; + SRTSOCKET m_socket = SRT_INVALID_SOCK; friend class Medium; public: @@ -440,10 +440,10 @@ class SrtMedium: public Medium { Verb() << "Closing SRT socket for " << uri(); lock_guard lk(access); - if (m_socket == SRT_ERROR) + if (m_socket == SRT_INVALID_SOCK) return; srt_close(m_socket); - m_socket = SRT_ERROR; + m_socket = SRT_INVALID_SOCK; } // Forwarded in order to separate the implementation from @@ -624,16 +624,16 @@ void SrtMedium::CreateListener() sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int stat = srt_bind(m_socket, sa.get(), sizeof sa); + SRTSTATUS stat = srt_bind(m_socket, sa.get(), sizeof sa); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_bind"); } stat = srt_listen(m_socket, backlog); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_listen"); @@ -674,7 +674,7 @@ unique_ptr SrtMedium::Accept() { sockaddr_any sa; SRTSOCKET s = srt_accept(m_socket, (sa.get()), (&sa.len)); - if (s == SRT_ERROR) + if (s == SRT_INVALID_SOCK) { Error(UDT::getlasterror(), "srt_accept"); } @@ -734,8 +734,8 @@ void SrtMedium::Connect() { sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int st = srt_connect(m_socket, sa.get(), sizeof sa); - if (st == SRT_ERROR) + SRTSOCKET st = srt_connect(m_socket, sa.get(), sizeof sa); + if (st == SRT_INVALID_SOCK) Error(UDT::getlasterror(), "srt_connect"); ConfigurePost(m_socket); @@ -766,7 +766,7 @@ int SrtMedium::ReadInternal(char* w_buffer, int size) do { st = srt_recv(m_socket, (w_buffer), size); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { int syserr; if (srt_getlasterror(&syserr) == SRT_EASYNCRCV && !m_broken) @@ -885,7 +885,7 @@ Medium::ReadStatus Medium::Read(bytevector& w_output) void SrtMedium::Write(bytevector& w_buffer) { int st = srt_send(m_socket, w_buffer.data(), (int)w_buffer.size()); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { Error(UDT::getlasterror(), "srt_send"); } diff --git a/common/devel_util.h b/common/devel_util.h index 969390993..3c00ec356 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -1,4 +1,11 @@ +#include + +template +concept Streamable = requires(OS& os, T value) { + { os << value }; +}; + template struct IntWrapper { @@ -22,16 +29,23 @@ struct IntWrapper return v; } - template - friend T& operator<<(T& out, const IntWrapper& x) + bool operator<(const IntWrapper& w) const + { + return v < w.v; + } + + template + requires Streamable + friend Str& operator<<(Str& out, const IntWrapper& x) { out << x.v; return out; } - bool operator<(const IntWrapper& w) const + friend std::ostream& operator<<(std::ostream& out, const IntWrapper& x) { - return v < w.v; + out << x.v; + return out; } }; @@ -72,3 +86,4 @@ typedef IntWrapper SRTSOCKET; typedef IntWrapper SRTSTATUS; typedef IntWrapperLoose SRTSTATUS_LOOSE; + diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 076db0e5c..bcf7653eb 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -233,7 +233,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return ""; std::ostringstream os; - os << "@" << sock << ":"; + os << "@" << int(sock) << ":"; return os.str(); } diff --git a/srtcore/core.h b/srtcore/core.h index 16867404f..33f07195f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -297,7 +297,7 @@ class CUDT { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_SocketID << ": "; + os << "@" << int(m_SocketID) << ": "; return os.str(); #else return ""; diff --git a/srtcore/group.h b/srtcore/group.h index ead45707e..68b999961 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -740,7 +740,7 @@ class CUDTGroup { #if ENABLE_LOGGING std::ostringstream os; - os << "$" << m_GroupID << ":"; + os << "$" << int(m_GroupID) << ":"; return os.str(); #else return ""; diff --git a/srtcore/srt.h b/srtcore/srt.h index fc82ce8e9..682a6fe26 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -134,12 +134,21 @@ written by #define SRT_ATR_DEPRECATED #endif -// Invert this in order to retest if the symbolic constants +// Unblock this in order to retest if the symbolic constants // have been used properly. With this change the compiler will // detect every case when it wasn't. +// Important: you need to --use-c++-std=c++20 to compile SRT +// with this enabled. +//#define SRT_TEST_FORCED_CONSTANT + +#ifndef SRT_TEST_FORCED_CONSTANT +// This is normal and should be normally used. typedef int32_t SRTSOCKET; typedef int SRTSTATUS; -//#include "../common/devel_util.h" +#else +// Used for development only. +#include "../common/devel_util.h" +#endif #ifdef __cplusplus diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 9804eb3f6..011f277ae 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -75,7 +75,7 @@ SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::C SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } -SRTSTATUS srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSOCKET srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; } #endif diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 6fd284c23..cf363305a 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -64,21 +64,21 @@ enum TEST_CASE struct TestResultNonBlocking { - int connect_ret; - int accept_ret; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; int epoll_wait_ret; int epoll_event; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state [CHECK_SOCKET_COUNT]; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state [CHECK_SOCKET_COUNT]; }; struct TestResultBlocking { - int connect_ret; - int accept_ret; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state[CHECK_SOCKET_COUNT]; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state[CHECK_SOCKET_COUNT]; }; @@ -137,35 +137,37 @@ static const std::string s_pwd_no(""); */ const int IGNORE_EPOLL = -2; -const int IGNORE_SRTS = -1; +const SRT_SOCKSTATUS IGNORE_SRTS = (SRT_SOCKSTATUS)-1; +const SRT_KM_STATE IGNORE_KMSTATE = (SRT_KM_STATE)-1; +const SRTSOCKET IGNORE_ACCEPT = (SRTSOCKET)-2; const TestCaseNonBlocking g_test_matrix_non_blocking[] = { - // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -185,31 +187,31 @@ const TestCaseNonBlocking g_test_matrix_non_blocking[] = */ const TestCaseBlocking g_test_matrix_blocking[] = { - // ENFORCEDENC | Password | | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -240,13 +242,13 @@ class TestEnforcedEncryption ASSERT_NE(m_caller_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_SENDER, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_caller_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); m_listener_socket = srt_create_socket(); ASSERT_NE(m_listener_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SENDER, &s_no, sizeof s_no), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_listener_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); // Will use this epoll to wait for srt_accept(...) const int epoll_out = SRT_EPOLL_IN | SRT_EPOLL_ERR; @@ -265,10 +267,10 @@ class TestEnforcedEncryption public: - int SetEnforcedEncryption(PEER_TYPE peer, bool value) + SRTSTATUS SetEnforcedEncryption(PEER_TYPE peer, bool value) { const SRTSOCKET &socket = peer == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); + return srt_setsockflag(socket, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); } @@ -277,15 +279,15 @@ class TestEnforcedEncryption const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; bool optval; int optlen = sizeof optval; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_STATUS_OK); return optval ? true : false; } - int SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) + SRTSTATUS SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) { const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); + return srt_setsockflag(socket, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); } @@ -293,8 +295,8 @@ class TestEnforcedEncryption { int km_state = 0; int opt_size = sizeof km_state; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_SUCCESS); - + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_STATUS_OK); + return km_state; } @@ -303,7 +305,7 @@ class TestEnforcedEncryption { int val = 0; int size = sizeof val; - EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_STATUS_OK); return val; } @@ -322,26 +324,26 @@ class TestEnforcedEncryption const bool is_blocking = std::is_same::value; if (is_blocking) { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); } else { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode } // Prepare input state const TestCase &test = GetTestMatrix(test_case); - ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_STATUS_OK); - ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_STATUS_OK); // Determine the subcase for the KLUDGE (check the behavior of the decryption failure) const bool case_pw_failure = test.password[PEER_CALLER] != test.password[PEER_LISTENER]; @@ -361,7 +363,7 @@ class TestEnforcedEncryption ASSERT_NE(srt_bind(m_listener_socket, psa, sizeof sa), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_socket, 4), SRT_ERROR); - SRTSOCKET accepted_socket = -1; + SRTSOCKET accepted_socket = SRT_INVALID_SOCK; auto accepting_thread = std::thread([&] { const int epoll_event = WaitOnEpoll(expect); @@ -390,12 +392,12 @@ class TestEnforcedEncryption std::cerr << "[T] ACCEPT SUCCEEDED: @" << accepted_socket << "\n"; } - EXPECT_NE(accepted_socket, 0); + EXPECT_NE(accepted_socket, SRT_SOCKID_CONNREQ); if (expect.accept_ret == SRT_INVALID_SOCK) { EXPECT_EQ(accepted_socket, SRT_INVALID_SOCK); } - else if (expect.accept_ret != -2) + else if (expect.accept_ret != IGNORE_ACCEPT) { EXPECT_NE(accepted_socket, SRT_INVALID_SOCK); } @@ -441,10 +443,10 @@ class TestEnforcedEncryption } }); - const int connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); + const SRTSOCKET connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); EXPECT_EQ(connect_ret, expect.connect_ret); - if (connect_ret == SRT_ERROR && connect_ret != expect.connect_ret) + if (connect_ret == SRT_INVALID_SOCK && connect_ret != expect.connect_ret) { std::cerr << "UNEXPECTED! srt_connect returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; @@ -516,7 +518,7 @@ class TestEnforcedEncryption std::cout << "W: " << epoll_res_w << std::endl; char buffer[1316] = {1, 2, 3, 4}; - ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), SRT_ERROR); + ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), int(SRT_ERROR)); std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -615,13 +617,13 @@ int TestEnforcedEncryption::WaitOnEpoll(const TestResultN } std::cerr << std::endl; - // Expect: -2 means that + // Expect: IGNORE_EPOLL means that you should not check the result. if (expect.epoll_wait_ret != IGNORE_EPOLL) { EXPECT_EQ(epoll_res, expect.epoll_wait_ret); } - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; return 0; @@ -687,8 +689,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) { #ifdef SRT_ENABLE_ENCRYPTION // Empty string sets password to none - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_STATUS_OK); EXPECT_EQ(SetPassword(PEER_CALLER, std::string("too_short")), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("too_short")), SRT_ERROR); @@ -706,8 +708,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) EXPECT_EQ(SetPassword(PEER_CALLER, long_pwd), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, long_pwd), SRT_ERROR); - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_STATUS_OK); #else EXPECT_EQ(SetPassword(PEER_CALLER, "whateverpassword"), SRT_ERROR); #endif @@ -723,8 +725,8 @@ TEST_F(TestEnforcedEncryption, SetGetDefault) EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), true); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), true); - EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_SUCCESS); - EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_SUCCESS); + EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_STATUS_OK); + EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_STATUS_OK); EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), false); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), false); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 72946b736..63b443493 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -22,9 +22,9 @@ TEST(CEPoll, InfiniteWait) ASSERT_EQ(srt_epoll_wait(epoll_id, nullptr, nullptr, nullptr, nullptr, -1, - 0, 0, 0, 0), SRT_ERROR); + 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, WaitNoSocketsInEpoll) @@ -41,9 +41,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -56,9 +56,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -67,11 +67,11 @@ TEST(CEPoll, WaitEmptyCall) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -80,9 +80,9 @@ TEST(CEPoll, WaitEmptyCall) ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); ASSERT_EQ(srt_epoll_wait(epoll_id, 0, NULL, 0, NULL, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, UWaitEmptyCall) @@ -90,11 +90,11 @@ TEST(CEPoll, UWaitEmptyCall) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -102,9 +102,9 @@ TEST(CEPoll, UWaitEmptyCall) const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); - ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -113,14 +113,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -136,9 +136,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -147,14 +147,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -165,9 +165,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -176,11 +176,11 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -189,7 +189,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) /* We intentionally pass the wrong socket ID. The error should be returned.*/ ASSERT_EQ(srt_epoll_add_usock(epoll_id + 1, client_sock, &epoll_out), SRT_ERROR); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -199,14 +199,14 @@ TEST(CEPoll, HandleEpollEvent) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -224,7 +224,7 @@ TEST(CEPoll, HandleEpollEvent) set* rval = &readset; set* wval = &writeset; - ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), SRT_ERROR); + ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), int(SRT_ERROR)); try { @@ -260,19 +260,19 @@ TEST(CEPoll, NotifyConnectionBreak) // 1. Prepare client SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes SRT_ATR_UNUSED = 1; const int no SRT_ATR_UNUSED = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int client_epoll_id = srt_epoll_create(); ASSERT_GE(client_epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; /* We intentionally pass the wrong socket ID. The error should be returned.*/ - EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_SUCCESS); + EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_STATUS_OK); sockaddr_in sa_client; memset(&sa_client, 0, sizeof sa_client); @@ -282,10 +282,10 @@ TEST(CEPoll, NotifyConnectionBreak) // 2. Prepare server SRTSOCKET server_sock = srt_create_socket(); - ASSERT_NE(server_sock, SRT_ERROR); + ASSERT_NE(server_sock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int server_epoll_id = srt_epoll_create(); ASSERT_GE(server_epoll_id, 0); @@ -320,13 +320,13 @@ TEST(CEPoll, NotifyConnectionBreak) 0, 0, 0, 0); EXPECT_EQ(epoll_res, 1); - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; } // Wait for the caller connection thread to return connection result - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); sockaddr_in scl; int sclen = sizeof scl; @@ -343,15 +343,15 @@ TEST(CEPoll, NotifyConnectionBreak) this_thread::sleep_for(chrono::seconds(1)); cout << "(async call): Closing client connection\n"; return srt_close(client_sock); - }); + }); int timeout_ms = -1; - int ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; + SRTSOCKET ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; int len = 2; cout << "TEST: entering INFINITE WAIT\n"; const int epoll_wait_res = srt_epoll_wait(epoll_io, ready, &len, nullptr, nullptr, timeout_ms, 0, 0, 0, 0); cout << "TEST: return from INFINITE WAIT\n"; - if (epoll_wait_res == SRT_ERROR) + if (epoll_wait_res == int(SRT_ERROR)) cerr << "socket::read::epoll " << to_string(srt_getlasterror(nullptr)); EXPECT_EQ(epoll_wait_res, 1); EXPECT_EQ(len, 1); @@ -359,7 +359,7 @@ TEST(CEPoll, NotifyConnectionBreak) // Wait for the caller to close connection // There should be no wait, as epoll should wait until connection is closed. - EXPECT_EQ(close_res.get(), SRT_SUCCESS); + EXPECT_EQ(close_res.get(), SRT_STATUS_OK); const SRT_SOCKSTATUS state = srt_getsockstate(sock); const bool state_valid = state == SRTS_BROKEN || state == SRTS_CLOSING || state == SRTS_CLOSED; EXPECT_TRUE(state_valid); @@ -374,14 +374,14 @@ TEST(CEPoll, HandleEpollEvent2) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -435,14 +435,14 @@ TEST(CEPoll, HandleEpollNoEvent) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -485,11 +485,11 @@ TEST(CEPoll, ThreadedUpdate) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect CEPoll epoll; const int epoll_id = epoll.create(); @@ -550,8 +550,8 @@ class TestEPoll: public srt::Test { protected: - int m_client_pollid = SRT_ERROR; - SRTSOCKET m_client_sock = SRT_ERROR; + int m_client_pollid = int(SRT_ERROR); + SRTSOCKET m_client_sock = SRT_INVALID_SOCK; void clientSocket() { @@ -559,12 +559,12 @@ class TestEPoll: public srt::Test int no = 0; m_client_sock = srt_create_socket(); - ASSERT_NE(m_client_sock, SRT_ERROR); + ASSERT_NE(m_client_sock, SRT_INVALID_SOCK); - 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_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); + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_out = SRT_EPOLL_OUT; srt_epoll_add_usock(m_client_pollid, m_client_sock, &epoll_out); @@ -578,7 +578,7 @@ class TestEPoll: public srt::Test sockaddr* psa = (sockaddr*)&sa; - ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_ERROR); + ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_INVALID_SOCK); // Socket readiness for connection is checked by polling on WRITE allowed sockets. @@ -594,7 +594,7 @@ class TestEPoll: public srt::Test 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); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 0); // get exactly one write event without reads ASSERT_EQ(wlen, 1); // get exactly one write event without reads @@ -606,11 +606,11 @@ class TestEPoll: public srt::Test -1, // infinit ttl true // in order must be set to true ), - SRT_ERROR); + int(SRT_ERROR)); // disable receiving OUT events int epoll_err = SRT_EPOLL_ERR; - ASSERT_EQ(0, srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err)); + ASSERT_EQ(srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err), SRT_STATUS_OK); { int rlen = 2; SRTSOCKET read[2]; @@ -623,11 +623,11 @@ class TestEPoll: public srt::Test 1000, 0, 0, 0, 0)); const int last_error = srt_getlasterror(NULL); - EXPECT_EQ(SRT_ETIMEOUT, last_error) << last_error; + EXPECT_EQ(last_error, (int)SRT_ETIMEOUT) << last_error; } } - int m_server_pollid = SRT_ERROR; + int m_server_pollid = int(SRT_ERROR); void createServerSocket(SRTSOCKET& w_servsock) { @@ -635,10 +635,10 @@ class TestEPoll: public srt::Test int no = 0; SRTSOCKET servsock = srt_create_socket(); - ASSERT_NE(servsock, SRT_ERROR); + ASSERT_NE(servsock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(servsock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(servsock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_in = SRT_EPOLL_IN; srt_epoll_add_usock(m_server_pollid, servsock, &epoll_in); @@ -672,7 +672,7 @@ class TestEPoll: public srt::Test 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 ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -699,7 +699,7 @@ class TestEPoll: public srt::Test 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 ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -737,10 +737,10 @@ class TestEPoll: public srt::Test void setup() override { m_client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_client_pollid); + ASSERT_NE(m_client_pollid, int(SRT_ERROR)); m_server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_server_pollid); + ASSERT_NE(m_server_pollid, int(SRT_ERROR)); } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index ece753928..b0b8ac8a8 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -307,8 +307,8 @@ TEST(TestFEC, Connection) // 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); + ASSERT_NE(a, SRT_INVALID_SOCK); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -360,7 +360,7 @@ TEST(TestFEC, ConnectionReorder) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -412,7 +412,7 @@ TEST(TestFEC, ConnectionFull1) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -464,7 +464,7 @@ TEST(TestFEC, ConnectionFull2) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -516,7 +516,7 @@ TEST(TestFEC, ConnectionMess) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -566,7 +566,7 @@ TEST(TestFEC, ConnectionForced) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 97f9e684a..7f22df648 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -50,21 +50,22 @@ TEST(Transmission, FileUpload) // Find unused a port not used by any other service. // Otherwise srt_connect may actually connect. - int bind_res = -1; + SRTSTATUS bind_res = SRT_ERROR; 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) + int bind_err = srt_getlasterror(NULL); + if (bind_res == SRT_STATUS_OK) { 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_TRUE(bind_err == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_err; } - ASSERT_GE(bind_res, 0); + ASSERT_GE((int)bind_res, 0); srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index ee11292b0..18fbf7595 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -30,12 +30,12 @@ class TestIPv6 void setup() override { m_caller_sock = srt_create_socket(); - ASSERT_NE(m_caller_sock, SRT_ERROR); + ASSERT_NE(m_caller_sock, SRT_INVALID_SOCK); // IPv6 calling IPv4 would otherwise fail if the system-default net.ipv6.bindv6only=1. ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_IPV6ONLY, &no, sizeof no), SRT_ERROR); m_listener_sock = srt_create_socket(); - ASSERT_NE(m_listener_sock, SRT_ERROR); + ASSERT_NE(m_listener_sock, SRT_INVALID_SOCK); } void teardown() override diff --git a/testing/srt-test-file.cpp b/testing/srt-test-file.cpp index 3dd8fe998..baacc66d1 100644 --- a/testing/srt-test-file.cpp +++ b/testing/srt-test-file.cpp @@ -261,7 +261,7 @@ bool DoUpload(UriParser& ut, string path, string filename) { int st = srt_send(ss, buf.data()+shift, int(n)); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; return false; @@ -290,7 +290,7 @@ bool DoUpload(UriParser& ut, string path, string filename) size_t bytes; size_t blocks; int st = srt_getsndbuffer(ss, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; return false; @@ -358,7 +358,7 @@ bool DoDownload(UriParser& us, string directory, string filename) for (;;) { int n = srt_recv(ss, buf.data(), int(::g_buffer_size)); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; return false; diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..bfccb3f1c 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -298,7 +298,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr { SRT_GROUP_TYPE gt; size = sizeof gt; - if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) + if (SRT_ERROR != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { if (gt < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; From 7a4e00ca2259935a8bf1e9785c7ae1baeeb73297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 10 Oct 2023 14:58:03 +0200 Subject: [PATCH 069/174] Fixed clang warnings (build break on some CI) --- apps/socketoptions.hpp | 4 ++-- testing/srt-test-live.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 12694f091..e6de4db17 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -58,7 +58,7 @@ struct SocketOption bool applyt(Object socket, std::string value) const; template - static int setso(Object socket, int protocol, int symbol, const void* data, size_t size) + static int setso(Object , int , int , const void* , size_t) { typename Object::wrong_version error; return -1; @@ -71,7 +71,7 @@ struct SocketOption template<> inline int SocketOption::setso(SRTSOCKET socket, int /*ignored*/, int sym, const void* data, size_t size) { - return (int)srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_setsockflag(socket, SRT_SOCKOPT(sym), data, (int) size); } #if ENABLE_BONDING diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index bfccb3f1c..a68f81612 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (SRT_ERROR != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 6827df78cc22ccb8ea2ce75b2b45290e00488ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Oct 2023 15:11:02 +0200 Subject: [PATCH 070/174] Added tests for more cases and fixed remaining failures. STILL mutex not enabled. --- srtcore/api.cpp | 20 ++++++- srtcore/group.cpp | 1 + srtcore/group.h | 21 +++++-- test/test_epoll.cpp | 135 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 142 insertions(+), 35 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 9f592a07a..a1e1abb14 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -697,6 +697,12 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } } + // This is set to true only if the group is to be reported + // from the accept call for the first time. Once it is extracted + // this way, this flag is cleared. + if (should_submit_to_accept) + g->m_bPending = true; + // Update the status in the group so that the next // operation can include the socket in the group operation. CUDTGroup::SocketData* gm = ns->m_GroupMemberData; @@ -850,8 +856,8 @@ SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() // matter is simple - if it's present, you light up the // SRT_EPOLL_ACCEPT flag. -//#if !ENABLE_BONDING -#if 1 +#if !ENABLE_BONDING +//#if 1 ScopedLock accept_lock (m_AcceptLock); // Make it simplified here - nonempty container = have acceptable sockets. @@ -891,7 +897,14 @@ int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) CUDTSocket* s = locateSocket_LOCKED(*i); if (!s) continue; // wiped in the meantime - ignore - if (s->m_GroupOf && s->m_GroupOf->groupConnected()) + + // If this pending socket is a group member, but the group + // to which it belongs is NOT waiting to be accepted, then + // light up the UPDATE event only. Light up ACCEPT only if + // this is a single socket, or this single socket has turned + // the mirror group to be first time available for accept(), + // and this accept() hasn't been done yet. + if (s->m_GroupOf && !s->m_GroupOf->groupPending()) flags |= SRT_EPOLL_UPDATE; else flags |= SRT_EPOLL_ACCEPT; @@ -1222,6 +1235,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int { u = s->m_GroupOf->m_GroupID; s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure + s->m_GroupOf->m_bPending = false; // Mark the beginning of the connection at the moment // when the group ID is returned to the app caller diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c6b92a0ff..d70a32ba4 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -273,6 +273,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_RcvBaseSeqNo(SRT_SEQNO_NONE) , m_bOpened(false) , m_bConnected(false) + , m_bPending(false) , m_bClosing(false) , m_iLastSchedSeqNo(SRT_SEQNO_NONE) , m_iLastSchedMsgNo(SRT_MSGNO_NONE) diff --git a/srtcore/group.h b/srtcore/group.h index d31d9e610..90dcace16 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -203,9 +203,9 @@ class CUDTGroup return m_Group.empty(); } - bool groupConnected() + bool groupPending() { - return m_bConnected; + return m_bPending; } void setGroupConnected(); @@ -664,8 +664,21 @@ class CUDTGroup // from the first delivering socket will be taken as a good deal. sync::atomic m_RcvBaseSeqNo; - bool m_bOpened; // Set to true when at least one link is at least pending - bool m_bConnected; // Set to true on first link confirmed connected + /// True: at least one socket has joined the group in at least pending state + bool m_bOpened; + + /// True: at least one socket is connected, even if pending from the listener + bool m_bConnected; + + /// True: this group was created on the listner side for the first socket + /// that is pending connection, so the group is about to be reported for the + /// srt_accept() call, but the application hasn't retrieved the group yet. + /// Not in use in case of caller-side groups. + // NOTE: using atomic in otder to allow this variable to be changed independently + // on any mutex locks. + sync::atomic m_bPending; + + /// True: the group was requested to close and it should not allow any operations. bool m_bClosing; // There's no simple way of transforming config diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 99244642a..46cb10dce 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -545,80 +545,159 @@ TEST(CEPoll, ThreadedUpdate) } } -TEST(CEPoll, LateListenerReady) +void testListenerReady(const bool LATE_CALL, size_t nmembers) { - ASSERT_EQ(srt_startup(), 0); - - int server_sock = srt_create_socket(), caller_sock = 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); + TestInit init; + + SRTSOCKET server_sock, caller_sock; + server_sock = srt_create_socket(); + + if (nmembers > 0) + { + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + } + else + { + caller_sock = srt_create_socket(); + nmembers = 1; // Set to 1 so that caller starts at least once. + } + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); - srt_listen(server_sock, 1); + srt_listen(server_sock, nmembers+1); srt::setopt(server_sock)[SRTO_RCVSYN] = false; // Ok, the listener socket is ready; now make a call, but // do not do anything on the listener socket yet. -// This macro is to manipulate with the moment when the call is made -// towards the eid subscription. If 1, then the call is made first, -// and then subsciption after a 1s time. Set it to 0 to see how it -// works when the subscription is made first, so the readiness is from -// the listener changing the state. -#define LATE_CALL 1 + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; -#if LATE_CALL + std::vector> connect_res; - // We don't need the caller to be async, it can hang up here. - auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + if (LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } -#endif + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Sleeping 1s...\n"; this_thread::sleep_for(chrono::milliseconds(1000)); // What is important is that the accepted socket is now reporting in // on the listener socket. So let's create an epoll. int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); // and add this listener to it int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); -#if !LATE_CALL - - // We don't need the caller to be async, it can hang up here. - auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + if (!LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } -#endif + std::cout << "STARTED connecting...\n"; + } + std::cout << "Waiting for readiness...\n"; // And see now if the waiting accepted socket reports it. SRT_EPOLL_EVENT fdset[1]; EXPECT_EQ(srt_epoll_uwait(eid, fdset, 1, 5000), 1); + std::cout << "Accepting...\n"; sockaddr_in scl; int sclen = sizeof scl; SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen); EXPECT_NE(sock, SRT_INVALID_SOCK); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + if (nmembers > 1) + { + std::cout << "With >1 members, check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + EXPECT_EQ(srt_epoll_uwait(eid_postcheck, fdset, 1, 5000), 1); + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + } + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < nmembers; ++i) + { + EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + } srt_epoll_release(eid); - srt_close(sock); + srt_epoll_release(eid_postcheck); + srt_close(server_sock); srt_close(caller_sock); + srt_close(sock); +} - EXPECT_EQ(srt_cleanup(), 0); +TEST(CEPoll, EarlyListenerReady) +{ + testListenerReady(false, 0); } +TEST(CEPoll, LateListenerReady) +{ + testListenerReady(true, 0); +} + +#if ENABLE_BONDING + +TEST(CEPoll, EarlyGroupListenerReady_1) +{ + testListenerReady(false, 1); +} + +TEST(CEPoll, LateGroupListenerReady_1) +{ + testListenerReady(true, 1); +} + +TEST(CEPoll, EarlyGroupListenerReady_3) +{ + testListenerReady(false, 3); +} + +TEST(CEPoll, LateGroupListenerReady_3) +{ + testListenerReady(true, 3); +} + +#endif class TestEPoll: public srt::Test { From 039291cda0aeca208f77e720f41a08c5aac265a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Oct 2023 16:29:23 +0200 Subject: [PATCH 071/174] Fixed the problem with locking --- srtcore/api.cpp | 21 ++++++++++++--------- srtcore/api.h | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a1e1abb14..cf524ea14 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -890,8 +890,6 @@ int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) // that they should re-read the group list and re-check readiness. // Now we can do lock once and for all - //ScopedLock glk (m_GlobControlLock); // XXX DEADLOCKS - for (set::iterator i = sockets.begin(); i != sockets.end(); ++i) { CUDTSocket* s = locateSocket_LOCKED(*i); @@ -2438,14 +2436,19 @@ int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int } #endif - CUDTSocket* s = locateSocket(u); - if (s) - { - ret = epoll_add_usock_INTERNAL(eid, s, events); - } - else + // The call to epoll_add_usock_INTERNAL is expected + // to be called under m_GlobControlLock, so use this lock here, too. { - throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + ScopedLock cs (m_GlobControlLock); + CUDTSocket* s = locateSocket_LOCKED(u); + if (s) + { + ret = epoll_add_usock_INTERNAL(eid, s, events); + } + else + { + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + } } return ret; diff --git a/srtcore/api.h b/srtcore/api.h index c049efde9..d08dca5b5 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -275,7 +275,10 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); +#if ENABLE_BONDING + SRT_ATTR_REQUIRES(m_GlobControlLock) int checkQueuedSocketsEvents(const std::set& sockets); +#endif int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); From bd9c4de9300ca9e8644a54f74596a8ed0a52ae0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 11:10:05 +0200 Subject: [PATCH 072/174] Fixed a problem: invalid per-listener report of member connections. Any pending connection can accept a group. Removed other pending sockets after accepting a group. Fixed tests to handle options also with RAII srt initializer --- docs/API/API-functions.md | 56 +++++++++-- srtcore/api.cpp | 110 +++++++++++++++++--- srtcore/api.h | 1 + srtcore/core.cpp | 3 +- srtcore/epoll.cpp | 25 ++++- srtcore/group.cpp | 20 ++++ srtcore/group.h | 16 ++- test/test_env.h | 7 +- test/test_epoll.cpp | 205 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 410 insertions(+), 33 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 90ed257dd..182ecdebc 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -652,16 +652,52 @@ 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`](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 -group, as well as sockets for them, will be created in the background. The -[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) event is raised on the `lsn` socket when -a new background connection is attached to the group, although it's usually for -internal use only. +Note that this event might sometimes be spurious in case when the link for +the pending connection gets broken before the accepting operation is finished. +The `SRT_EPOLL_IN` flag set for the listener socket is still not a guarantee +that the following call to `srt_accept` will succeed. + +If the listener socket is allowed to accept group connections, if it is set the +[`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag, then +a group ID will be returned, if it's a pending group connection. This can be +recognized by checking if `SRTGROUP_MASK` is set on the returned value. +Accepting a group connection differs to accepting a single socket connection +by that: + +1. The connection is reported only for the very first socket that has been +successfully connected. Only for this case will the `SRT_EPOLL_IN` flag be +set on the listener and only in this case will the `srt_accept` call report +the connection. + +2. Further member connections of the group that has been already once accepted +will be handled in the background, and the listener socket will no longer get +the `SRT_EPOLL_IN` flag set when it happens. Instead the +[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag will be set. This flag is +edge-triggered-only because there is no operation to be performed in response +that could make this flag cleared. It's mostly used internally and the +application may use it to update its group data cache. + +3. If your application has created more than one listener socket that has +allowed group connections, every newly connected socket that is a member of an +already connected group will join this group no matter to which listener +socket it was reported. If you want to use this feature, you should take special +care of how you perform the accept operation. The group connection may be +accepted off any of these listener sockets, but still only once. It is then +recommended: + + * In non-blocking mode, poll on all listener sockets that are expected to + get a group connection at once + + * In blocking mode, use `srt_accept_bond` instead (it uses epoll internally) + +Note also that in this case there are more chances for `SRT_EPOLL_IN` flag +to be spurious. For example, if you have two listener sockets configured for +group connection and on each of them there's a pending connection, you will +have `SRT_EPOLL_IN` flag set on both listener sockets. However, once you +accept the group connection on any of them, the other pending connection will +get automatically handled in the background and the existing `SRT_EPOLL_IN` +flag will be spurious (calling `srt_accept` on that listener socket will fail). + | Returns | | |:----------------------------- |:----------------------------------------------------------------------- | diff --git a/srtcore/api.cpp b/srtcore/api.cpp index cf524ea14..ad8bb154d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -681,6 +681,17 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, goto ERR_ROLLBACK; } + // Acceptance of the group will have to be done through accepting + // of one of the pending sockets. There can be, however, multiple + // such sockets at a time, some of them might get broken before + // being accepted, and therefore we need to make all sockets ready. + // But then, acceptance of a group may happen only once, so if any + // sockets of the same group were submitted to accept, they must + // be removed from the accept queue at this time. + should_submit_to_accept = g->groupPending(); + + /* XXX remove if no longer informational + // 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 @@ -696,12 +707,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, break; } } - - // This is set to true only if the group is to be reported - // from the accept call for the first time. Once it is extracted - // this way, this flag is cleared. - if (should_submit_to_accept) - g->m_bPending = true; + */ // Update the status in the group so that the next // operation can include the socket in the group operation. @@ -714,11 +720,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, gm->rcvstate = SRT_GST_IDLE; gm->laststatus = SRTS_CONNECTED; - if (!g->m_bConnected) - { - HLOGC(cnlog.Debug, log << "newConnection(GROUP): First socket connected, SETTING GROUP CONNECTED"); - g->m_bConnected = true; - } + g->setGroupConnected(); // XXX PROLBEM!!! These events are subscribed here so that this is done once, lazily, // but groupwise connections could be accepted from multiple listeners for the same group! @@ -1231,13 +1233,16 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int // it's a theoretically possible scenario if (s->m_GroupOf) { - u = s->m_GroupOf->m_GroupID; - s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure - s->m_GroupOf->m_bPending = false; - + CUDTGroup* g = s->m_GroupOf; // 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(); + g->m_stats.tsLastSampleTime = steady_clock::now(); + + HLOGC(cnlog.Debug, log << "accept: reporting group $" << g->m_GroupID << " instead of member socket @" << u); + u = g->m_GroupID; + s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure + g->m_bPending = false; + CUDT::uglobal().removePendingForGroup(g); } else { @@ -1263,6 +1268,79 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int return u; } +#if ENABLE_BONDING + +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::removePendingForGroup(const CUDTGroup* g) +{ + // We don't have a list of listener sockets that have ever + // reported a pending connection for a group, so the only + // way to find them is to ride over the list of all sockets... + + list members; + g->getMemberSockets((members)); + + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++i) + { + CUDTSocket* s = i->second; + // Check if any of them is a listener socket... + + /* XXX This is left for information only that we are only + interested with listener sockets - with the current + implementation checking it is pointless because the + m_QueuedSockets structure is present in every socket + anyway even if it's not a listener, and only listener + sockets may have this container nonempty. So checking + the container should suffice. + + if (!s->core().m_bListening) + continue; + */ + + if (s->m_QueuedSockets.empty()) + continue; + + // Somehow fortunate for us that it's a set, so we + // can simply check if this allegedly listener socket + // contains any of them. + for (list::iterator m = members.begin(), mx = m; m != members.end(); m = mx) + { + ++mx; + std::set::iterator q = s->m_QueuedSockets.find(*m); + if (q != s->m_QueuedSockets.end()) + { + HLOGC(cnlog.Debug, log << "accept: listener @" << s->m_SocketID + << " had ququed member @" << *m << " -- removed"); + // Found an intersection socket. + // Remove it from the listener queue + s->m_QueuedSockets.erase(q); + + // NOTE ALSO that after this removal the queue may be EMPTY, + // and if so, the listener socket should be no longer ready for accept. + if (s->m_QueuedSockets.empty()) + { + m_EPoll.update_events(s->m_SocketID, s->core().m_sPollID, SRT_EPOLL_ACCEPT, false); + } + + // and remove it also from the members list. + // This can be done safely because we use a SAFE LOOP. + // We can also do it safely because a socket may be + // present in only one listener socket in the whole app. + members.erase(m); + } + } + + // It may happen that the list of members can be + // eventually purged even if we haven't checked every socket. + // If it happens so, quit immediately because there's nothing + // left to do. + if (members.empty()) + return; + } +} + +#endif + int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified diff --git a/srtcore/api.h b/srtcore/api.h index d08dca5b5..412375688 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -278,6 +278,7 @@ class CUDTUnited #if ENABLE_BONDING SRT_ATTR_REQUIRES(m_GlobControlLock) int checkQueuedSocketsEvents(const std::set& sockets); + void removePendingForGroup(const CUDTGroup* g); #endif int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fe30efa2b..c6b50292c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3300,7 +3300,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // This can only happen on a listener (it's only called on a site that is // HSD_RESPONDER), so it was a response for a groupwise connection. // Therefore such a group shall always be considered opened. - gp->setOpen(); + // It's also set pending and it stays this way until accepted. + gp->setOpenPending(); HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $" diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index e2b861bf9..80fe53a65 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -506,18 +506,23 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) + { + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " doesn't exist"); throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + } CEPollDesc& ed = p->second; if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty()) { // Empty EID is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is empty (use SRT_EPOLL_ENABLE_EMPTY to allow)"); throw CUDTException(MJ_NOTSUP, MN_EEMPTY); } if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0)) { - // Empty EID is not allowed, report error. + // Empty container is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: empty output container with E" << eid << " (use SRT_EPOLL_ENABLE_OUTPUTCHECK to allow)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -525,6 +530,7 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 { // XXX Add error log // uwait should not be used with EIDs subscribed to system sockets + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is subscribed to system sckets (not allowed for uwait)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -536,11 +542,20 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ++total; if (total > fdsSize) + { + HLOGC(ealog.Debug, log << "epoll_uwait: output container size=" << fdsSize << " insufficient to report all sockets"); break; + } fdsSet[pos] = *i; + IF_HEAVY_LOGGING(std::ostringstream out); + IF_HEAVY_LOGGING(out << "epoll_uwait: Notice: fd=" << i->fd << " events="); + IF_HEAVY_LOGGING(PrintEpollEvent(out, i->events, 0)); + + SRT_ATR_UNUSED const bool was_edge = ed.checkEdge(i++); // NOTE: potentially deletes `i` + IF_HEAVY_LOGGING(out << (was_edge ? "(^)" : "")); + HLOGP(ealog.Debug, out.str()); - ed.checkEdge(i++); // NOTE: potentially deletes `i` } if (total) return total; @@ -875,6 +890,12 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return -1; // still, ignored. } + if (uid == SRT_INVALID_SOCK || uid == 0) + { + LOGC(eilog.Fatal, log << "epoll/update: IPE: invalid 'uid' submitted for update!"); + return -1; + } + int nupdated = 0; vector lost; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d70a32ba4..f3931d1d6 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3944,6 +3944,7 @@ void CUDTGroup::setGroupConnected() { if (!m_bConnected) { + HLOGC(cnlog.Debug, log << "GROUP: First socket connected, SETTING GROUP CONNECTED"); // Switch to connected state and give appropriate signal m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); m_bConnected = true; @@ -4016,6 +4017,25 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } } +struct FExtractGroupID +{ + SRTSOCKET operator()(const groups::SocketData& d) + { + return d.id; + } +}; + +void CUDTGroup::getMemberSockets(std::list& w_ids) const +{ + ScopedLock gl (m_GroupLock); + + for (cgli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + w_ids.push_back(gi->id); + } +} + + void CUDTGroup::activateUpdateEvent(bool still_have_items) { // This function actually reacts on the fact that a socket diff --git a/srtcore/group.h b/srtcore/group.h index 90dcace16..3ee51bb47 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -101,6 +101,7 @@ class CUDTGroup typedef std::list group_t; typedef group_t::iterator gli_t; + typedef group_t::const_iterator cgli_t; typedef std::vector< std::pair > sendable_t; struct Sendstate @@ -402,7 +403,7 @@ class CUDTGroup void getGroupCount(size_t& w_size, bool& w_still_alive); srt::CUDTUnited& m_Global; - srt::sync::Mutex m_GroupLock; + mutable srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; SRTSOCKET m_PeerGroupID; @@ -431,6 +432,8 @@ class CUDTGroup gli_t begin() { return m_List.begin(); } gli_t end() { return m_List.end(); } + cgli_t begin() const { return m_List.begin(); } + cgli_t end() const { return m_List.end(); } bool empty() { return m_List.empty(); } void push_back(const SocketData& data) { m_List.push_back(data); ++m_SizeCache; } void clear() @@ -752,7 +755,14 @@ class CUDTGroup // Required after the call on newGroup on the listener side. // On the listener side the group is lazily created just before // accepting a new socket and therefore always open. - void setOpen() { m_bOpened = true; } + // However, after creation it will be still waiting for being + // extracted by the application in `srt_accept`, and until then + // it stays as pending. + void setOpenPending() + { + m_bOpened = true; + m_bPending = true; + } std::string CONID() const { @@ -812,6 +822,8 @@ class CUDTGroup void updateLatestRcv(srt::CUDTSocket*); + void getMemberSockets(std::list&) const; + // Property accessors SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID); SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, peerid, m_PeerGroupID); diff --git a/test/test_env.h b/test/test_env.h index 2e2f78f7d..2b26995dd 100644 --- a/test/test_env.h +++ b/test/test_env.h @@ -53,7 +53,11 @@ class TestInit static void start(int& w_retstatus); static void stop(); - TestInit() { start((ninst)); } + TestInit() + { + start((ninst)); + HandlePerTestOptions(); + } ~TestInit() { stop(); } void HandlePerTestOptions(); @@ -71,7 +75,6 @@ class Test: public testing::Test void SetUp() override final { init_holder.reset(new TestInit); - init_holder->HandlePerTestOptions(); setup(); } diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 46cb10dce..ec3cf7fd5 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,6 +12,36 @@ using namespace std; using namespace srt; +static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) +{ + static pair const namemap [] = { + make_pair(SRT_EPOLL_IN, "R"), + make_pair(SRT_EPOLL_OUT, "W"), + make_pair(SRT_EPOLL_ERR, "E"), + make_pair(SRT_EPOLL_UPDATE, "U") + }; + bool any = false; + + const int N = (int)Size(namemap); + + for (int i = 0; i < N; ++i) + { + if (events & namemap[i].first) + { + os << "["; + if (et_events & namemap[i].first) + os << "^"; + os << namemap[i].second << "]"; + any = true; + } + } + + if (!any) + os << "[]"; + + return os; +} + TEST(CEPoll, InfiniteWait) { @@ -697,8 +727,183 @@ TEST(CEPoll, LateGroupListenerReady_3) testListenerReady(true, 3); } + +void testMultipleListenerReady(const bool LATE_CALL) +{ + 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); + + sockaddr_in sa2; + memset(&sa2, 0, sizeof sa2); + sa2.sin_family = AF_INET; + sa2.sin_port = htons(5556); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa2.sin_addr), 1); + + TestInit init; + + SRTSOCKET server_sock, server_sock2, caller_sock; + server_sock = srt_create_socket(); + server_sock2 = srt_create_socket(); + + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + EXPECT_NE(srt_setsockflag(server_sock2, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, 3); + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + srt_bind(server_sock2, (sockaddr*)& sa2, sizeof(sa2)); + srt_listen(server_sock2, 3); + srt::setopt(server_sock2)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; + + std::vector> connect_res; + + if (LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + })); + + + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid, server_sock2, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock2, &modes_postcheck), SRT_ERROR); + + if (!LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + })); + + std::cout << "STARTED connecting...\n"; + } + + // Sleep to make sure that the connection process has started. + this_thread::sleep_for(chrono::milliseconds(100)); + + std::cout << "Waiting for readiness on @" << server_sock << " and @" << server_sock2 << "\n"; + // And see now if the waiting accepted socket reports it. + + // This time we should expect that the connection reports in + // on two listener sockets + SRT_EPOLL_EVENT fdset[2] = {}; + std::ostringstream out; + + int nready = srt_epoll_uwait(eid, fdset, 2, 5000); + EXPECT_EQ(nready, 2); + out << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + out << " @" << fdset[i].fd; + PrintEpollEvent(out, fdset[i].events); + } + out << std::endl; + std::cout << out.str(); + + std::cout << "Accepting...\n"; + sockaddr_in scl; + int sclen = sizeof scl; + + // We choose the SECOND one to extract the group connection. + SRTSOCKET sock = srt_accept(server_sock2, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + // Make sure this time that the accepted connection is a group. + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + + std::cout << "Check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + // Note that if this was done AFTER accepting, the UPDATE would + // be only set one one socket. + nready = srt_epoll_uwait(eid_postcheck, fdset, 1, 5000); + EXPECT_EQ(nready, 1); + + std::cout << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + std::cout << " @" << fdset[i].fd; + PrintEpollEvent(std::cout, fdset[i].events); + } + std::cout << std::endl; + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < connect_res.size(); ++i) + { + EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + } + + srt_epoll_release(eid); + srt_epoll_release(eid_postcheck); + + srt_close(server_sock); + srt_close(server_sock2); + srt_close(caller_sock); + srt_close(sock); +} + +TEST(CEPoll, EarlyGroupMultiListenerReady) +{ + testMultipleListenerReady(false); +} + +TEST(CEPoll, LateGroupMultiListenerReady) +{ + testMultipleListenerReady(true); +} + + + #endif + class TestEPoll: public srt::Test { protected: From 8d3c5ca795af950ff5bd492aff6bc647232ff9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 16:08:36 +0200 Subject: [PATCH 073/174] Updated documentation --- docs/API/API-functions.md | 166 ++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 68 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 182ecdebc..6269c4373 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -627,76 +627,106 @@ the listener socket to accept group connections SRTSOCKET srt_accept(SRTSOCKET lsn, struct sockaddr* addr, int* addrlen); ``` -Accepts a pending connection, then creates and returns a new socket or -group ID that handles this connection. The group and socket can be -distinguished by checking the `SRTGROUP_MASK` bit on the returned ID. - -* `lsn`: the listener socket previously configured by [`srt_listen`](#srt_listen) -* `addr`: the IP address and port specification for the remote party +Extracts the first connection request on the queue of pending connections for +the listening socket, `lsn`, then creates and returns a new socket or group ID +that handles this connection. The group and socket can be distinguished by +checking the `SRTGROUP_MASK` bit on the returned ID. Note that by default group +connections will be rejected - this feature can be only enabled on demand (see +below). + +* `lsn`: the listening socket +* `addr`: a location to store the remote IP address and port for the connection * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of the returned object -**NOTE:** `addr` is allowed to be NULL, in which case it's understood that the -application is not interested in the address from which the connection originated. -Otherwise `addr` should specify an object into which the address will be written, -and `addrlen` must also specify a variable to contain the object size. Note also -that in the case of group connection only the initial connection that -establishes the group connection is returned, together with its address. As -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`](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. - -Note that this event might sometimes be spurious in case when the link for -the pending connection gets broken before the accepting operation is finished. -The `SRT_EPOLL_IN` flag set for the listener socket is still not a guarantee -that the following call to `srt_accept` will succeed. - -If the listener socket is allowed to accept group connections, if it is set the -[`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag, then -a group ID will be returned, if it's a pending group connection. This can be -recognized by checking if `SRTGROUP_MASK` is set on the returned value. -Accepting a group connection differs to accepting a single socket connection -by that: - -1. The connection is reported only for the very first socket that has been -successfully connected. Only for this case will the `SRT_EPOLL_IN` flag be -set on the listener and only in this case will the `srt_accept` call report -the connection. - -2. Further member connections of the group that has been already once accepted -will be handled in the background, and the listener socket will no longer get -the `SRT_EPOLL_IN` flag set when it happens. Instead the -[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag will be set. This flag is -edge-triggered-only because there is no operation to be performed in response -that could make this flag cleared. It's mostly used internally and the -application may use it to update its group data cache. - -3. If your application has created more than one listener socket that has -allowed group connections, every newly connected socket that is a member of an -already connected group will join this group no matter to which listener -socket it was reported. If you want to use this feature, you should take special -care of how you perform the accept operation. The group connection may be -accepted off any of these listener sockets, but still only once. It is then -recommended: - - * In non-blocking mode, poll on all listener sockets that are expected to - get a group connection at once - - * In blocking mode, use `srt_accept_bond` instead (it uses epoll internally) - -Note also that in this case there are more chances for `SRT_EPOLL_IN` flag -to be spurious. For example, if you have two listener sockets configured for -group connection and on each of them there's a pending connection, you will -have `SRT_EPOLL_IN` flag set on both listener sockets. However, once you -accept the group connection on any of them, the other pending connection will -get automatically handled in the background and the existing `SRT_EPOLL_IN` -flag will be spurious (calling `srt_accept` on that listener socket will fail). +General requirements for parameter correctness: + +* `lsn` must be first [bound](#srt_bind) and [listening](#srt_listen) + +* `addr` may be NULL, or otherwise it must be a pointer to an object +that can be treated as an instance of `sockaddr_in` or `sockaddr_in6` + +* `addrlen` should be a pointer to a variable set to the size of the object +specified in `addr`, if `addr` is not NULL. Otherwise it's ignored. + +If `addr` is not NULL, the information about the source IP address and +port of the peer will be written into this object. Note that whichever +type of object is expected here (`sockaddr_in` or `sockaddr_in6`), it +depends on the address type used in the `srt_bind` call for `lsn`. +If unsure in a particular situation, it is recommended that you use +`sockaddr_storage` or `srt::sockaddr_any`. + +If the `lsn` listener socket is blocking mode (if +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, +which is default), the call will block until the incoming connection is ready +for extraction. Otherwise, the call always returns immediately, possibly with +failure, if there was no pending connection waiting on the listening socket +`lsn`. + +The listener socket can be checked for any pending connections prior to calling +`srt_accept` by checking the `SRT_EPOLL_ACCEPT` epoll event (which is an alias +to `SRT_EPOLL_IN`). This event might be spurious in certain cases though, for +example, when the connection has been closed by the peer or broken before the +application extracts it. The call to `srt_accept` would then still fail in +such a case. + +In order to allow the listening socket `lsn` to accept a group connection, +the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) socket option +for the listening socket must be set to 1. Note that single socket connections +can still be reported to that socket. The application can distinguish the socket +and group connection by checking the `SRTGROUP_MASK` bit on the returned +successful value. There are some important differences to single socket +connections: + +1. Accepting a group connection can be done only once per connection. The +actual connection reporter is a socket, like before, but once you call +`srt_accept` and receive this group ID, it is the group considered connected, +and any other member connections of the same group will be handled in the +background. + +2. If a group was extracted from the `srt_accept` call, the address reported in +`addr` parameter is still the address of the connection that has triggered the +group connection extraction. While the group is connected, potentially new +connections may be added and any existing ones get broken at any time. The +information about all member connections, that are active at the moment, can be +obtained at any time through [`srt_group_data`](#srt_group_data) or the data +filled by [`srt_sendmsg2`](#srt_sendmsg2) and [`srt_recvmsg2`](#srt_recvmsg2) +in the [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure. + +3. Listening sockets are not bound to groups anyhow. You can allow multiple +listening sockets to accept group connections and the connection extracted +from the listener, if it is declared to be a group member, will join its +group, no matter which of the listening sockets has received the connection +request. This feature is prone to more tricky rules, however: + + * If you use multiple listener sockets, all of them in blocking mode, + allowed for group connections, and receiving connection requests for + the same group at the moment, and you run one thread per `srt_accept` + call, it is undefined, which of them will extract the group ID + for the connection, but still only one will, while the others will + continue blocking. If you want to use only one thread for accepting + connections from potentially multiple listening sockets in the blocking + mode, you should use [`srt_accept_bond`](#srt_accept_bond) instead. + + * If at the moment multiple listener sockets have received connection + request and you query them all for readiness epoll flags (by calling + an epoll waiting function), all of them will get the `SRT_EPOLL_ACCEPT` + flag set, but still only one of them will return the group ID from the + `srt_accept` call. After this call, from all listener sockets in the + whole application the `SRT_EPOLL_ACCEPT` flag, that was set by the reason + of a pending connection for the same group, will be withdrawn (that is, + it will be cleared if there are no other pending connections). This is + then yet another situation when this flag can be spurious. + +4. If you query a listening socket for epoll flags after the `srt_accept` +function has once returned the group ID, the listening sockets that have +received new member connection requests within that group will report only the +[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag. This flag is edge-triggered-only +because there is no operation you can perform in response in order to clear +this flag. This flag is mostly used internally and the application may use it +if it would like to trigger updating the current group information due to +having one newly added member connection. + | Returns | | @@ -707,7 +737,7 @@ flag will be spurious (calling `srt_accept` on that listener socket will fail). | Errors | | |:--------------------------------- |:----------------------------------------------------------------------- | -| [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `addrlen`, when `addr` is not NULL | +| [`SRT_EINVPARAM`](#srt_einvparam) | Invalid `addr` or `addrlen` (see requirements in the begininng) | | [`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 in the non-blocking mode | From ab96b4d6a7cb2debb9710365ef2ba5b44025b937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 17:37:25 +0200 Subject: [PATCH 074/174] Weird warning/error on Mac --- test/test_epoll.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index ec3cf7fd5..0d4e29a67 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,7 +12,8 @@ using namespace std; using namespace srt; -static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) +namespace { +ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) { static pair const namemap [] = { make_pair(SRT_EPOLL_IN, "R"), @@ -41,6 +42,7 @@ static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) return os; } +} TEST(CEPoll, InfiniteWait) From 9b93a3a1ef36509632302b1d2cc8048009543ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 16 Oct 2023 11:26:06 +0200 Subject: [PATCH 075/174] Some cosmetic fixes. Fixes for CI build breaks --- srtcore/api.cpp | 1 - srtcore/common.cpp | 48 +++++++++++++++++++++++++++++++++++++++ srtcore/common.h | 19 ++-------------- srtcore/core.cpp | 2 -- srtcore/epoll.cpp | 31 ------------------------- srtcore/group.cpp | 9 -------- test/test_epoll.cpp | 31 ------------------------- testing/srt-test-live.cpp | 2 +- 8 files changed, 51 insertions(+), 92 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ad8bb154d..bbc7be2c9 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -859,7 +859,6 @@ SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() // SRT_EPOLL_ACCEPT flag. #if !ENABLE_BONDING -//#if 1 ScopedLock accept_lock (m_AcceptLock); // Make it simplified here - nonempty container = have acceptable sockets. diff --git a/srtcore/common.cpp b/srtcore/common.cpp index dc60b3db7..61ea56311 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -463,6 +463,54 @@ bool SrtParseConfig(const string& s, SrtConfig& w_config) return true; } + +std::string FormatLossArray(const std::vector< std::pair >& lra) +{ + std::ostringstream os; + + os << "[ "; + for (std::vector< std::pair >::const_iterator i = lra.begin(); i != lra.end(); ++i) + { + int len = CSeqNo::seqoff(i->first, i->second); + os << "%" << i->first; + if (len > 1) + os << "+" << len; + os << " "; + } + + os << "]"; + return os.str(); +} + +ostream& PrintEpollEvent(ostream& os, int events, int et_events) +{ + static pair const namemap [] = { + make_pair(SRT_EPOLL_IN, "R"), + make_pair(SRT_EPOLL_OUT, "W"), + make_pair(SRT_EPOLL_ERR, "E"), + make_pair(SRT_EPOLL_UPDATE, "U") + }; + bool any = false; + + const int N = (int)Size(namemap); + + for (int i = 0; i < N; ++i) + { + if (events & namemap[i].first) + { + os << "["; + if (et_events & namemap[i].first) + os << "^"; + os << namemap[i].second << "]"; + any = true; + } + } + + if (!any) + os << "[]"; + + return os; +} } // namespace srt namespace srt_logging diff --git a/srtcore/common.h b/srtcore/common.h index bfc0c1d96..276e47c7c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1430,23 +1430,8 @@ inline bool checkMappedIPv4(const sockaddr_in6& sa) return checkMappedIPv4(addr); } -inline std::string FormatLossArray(const std::vector< std::pair >& lra) -{ - std::ostringstream os; - - os << "[ "; - for (std::vector< std::pair >::const_iterator i = lra.begin(); i != lra.end(); ++i) - { - int len = CSeqNo::seqoff(i->first, i->second); - os << "%" << i->first; - if (len > 1) - os << "+" << len; - os << " "; - } - - os << "]"; - return os.str(); -} +std::string FormatLossArray(const std::vector< std::pair >& lra); +std::ostream& PrintEpollEvent(std::ostream& os, int events, int et_events = 0); } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c6b50292c..3ec9554b4 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11676,7 +11676,6 @@ void srt::CUDT::addEPoll(const int eid) m_sPollID.insert(eid); leaveCS(uglobal().m_EPoll.m_EPollLock); - //* if (m_bListening) { // A listener socket can only get readiness on SRT_EPOLL_ACCEPT @@ -11694,7 +11693,6 @@ void srt::CUDT::addEPoll(const int eid) // used for listening and nothing else. return; } - // */ if (!stillConnected()) return; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 80fe53a65..e6ef44897 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -71,12 +71,6 @@ modified by using namespace std; using namespace srt::sync; -#if ENABLE_HEAVY_LOGGING -namespace srt { -static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0); -} -#endif - namespace srt_logging { extern Logger eilog, ealog; @@ -977,31 +971,6 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const namespace srt { -static ostream& PrintEpollEvent(ostream& os, int events, int et_events) -{ - static pair const namemap [] = { - make_pair(SRT_EPOLL_IN, "R"), - make_pair(SRT_EPOLL_OUT, "W"), - make_pair(SRT_EPOLL_ERR, "E"), - make_pair(SRT_EPOLL_UPDATE, "U") - }; - - const int N = (int)Size(namemap); - - for (int i = 0; i < N; ++i) - { - if (events & namemap[i].first) - { - os << "["; - if (et_events & namemap[i].first) - os << "^"; - os << namemap[i].second << "]"; - } - } - - return os; -} - string DisplayEpollResults(const std::map& sockset) { typedef map fmap_t; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f3931d1d6..4704a66e5 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4017,14 +4017,6 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } } -struct FExtractGroupID -{ - SRTSOCKET operator()(const groups::SocketData& d) - { - return d.id; - } -}; - void CUDTGroup::getMemberSockets(std::list& w_ids) const { ScopedLock gl (m_GroupLock); @@ -4035,7 +4027,6 @@ void CUDTGroup::getMemberSockets(std::list& w_ids) const } } - void CUDTGroup::activateUpdateEvent(bool still_have_items) { // This function actually reacts on the fact that a socket diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 0d4e29a67..38624a62e 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,37 +12,6 @@ using namespace std; using namespace srt; -namespace { -ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) -{ - static pair const namemap [] = { - make_pair(SRT_EPOLL_IN, "R"), - make_pair(SRT_EPOLL_OUT, "W"), - make_pair(SRT_EPOLL_ERR, "E"), - make_pair(SRT_EPOLL_UPDATE, "U") - }; - bool any = false; - - const int N = (int)Size(namemap); - - for (int i = 0; i < N; ++i) - { - if (events & namemap[i].first) - { - os << "["; - if (et_events & namemap[i].first) - os << "^"; - os << namemap[i].second << "]"; - any = true; - } - } - - if (!any) - os << "[]"; - - return os; -} -} TEST(CEPoll, InfiniteWait) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..aa6b45d6a 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 33de3eb164f852c27ff2822089c0108631609d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 16 Oct 2023 12:57:52 +0200 Subject: [PATCH 076/174] Fixed rejection code naming --- srtcore/core.cpp | 4 ++-- srtcore/srt.h | 4 ++-- test/test_ipv6.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e3c29b651..22876663f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4711,7 +4711,7 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " << m_config.zExpPayloadSize); - m_RejectReason = SRT_REJ_SETTINGS; + m_RejectReason = SRT_REJ_CONFIG; return false; } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -5733,7 +5733,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " << m_config.zExpPayloadSize); - m_RejectReason = SRT_REJ_SETTINGS; + m_RejectReason = SRT_REJ_CONFIG; throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } diff --git a/srtcore/srt.h b/srtcore/srt.h index c5bd4ed5e..c29e2c506 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -569,11 +569,11 @@ enum SRT_REJECT_REASON SRT_REJ_CONGESTION, // incompatible congestion-controller type SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group - SRT_REJ_TIMEOUT, // connection timeout + SRT_REJ_TIMEOUT = 16,// connection timeout #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif - SRT_REJ_SETTINGS, // socket settings on both sides collide and can't be negotiated + SRT_REJ_CONFIG = 18, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e8c14141d..9843a0303 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -117,7 +117,7 @@ class TestIPv6 { // Version with expected failure EXPECT_EQ(connect_res, SRT_ERROR); - EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_CONFIG); srt_close(m_listener_sock); } std::cout << "Connect: exit\n"; From dc7f4372de47e328a3f68d88c8a061a1218ea6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 17 Oct 2023 14:56:29 +0200 Subject: [PATCH 077/174] Added an API function to control the payload size --- apps/transmitmedia.cpp | 57 +++++++++++++++++++++++++++++++++------- apps/transmitmedia.hpp | 1 + srtcore/api.cpp | 41 +++++++++++++++++++++++++++++ srtcore/api.h | 2 ++ srtcore/core.h | 1 + srtcore/socketconfig.cpp | 50 ++++++++++++++++++++++------------- srtcore/socketconfig.h | 2 ++ srtcore/srt.h | 6 ++--- srtcore/srt_c_api.cpp | 26 ++++++++++++------ testing/testmedia.cpp | 57 +++++++++++++++++++++++++++++++++------- testing/testmedia.hpp | 1 + 11 files changed, 195 insertions(+), 49 deletions(-) diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 9c9e88401..27bdda22b 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -179,7 +179,7 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } - int fam_to_limit_size = AF_INET6; // take the less one as default + unsigned int max_payload_size = 0; // Try to interpret host and adapter first sockaddr_any host_sa, adapter_sa; @@ -187,27 +187,31 @@ void SrtCommon::InitParameters(string host, map par) if (host != "") { host_sa = CreateAddr(host); - fam_to_limit_size = host_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (host_sa.family() == AF_UNSPEC) Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } if (adapter != "" && adapter != host) { adapter_sa = CreateAddr(adapter); - fam_to_limit_size = adapter_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (adapter_sa.family() == AF_UNSPEC) Error("Failed to interpret 'adapter' spec: " + adapter); if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) { Error("Both host and adapter specified and they use different IP versions"); } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } - if (fam_to_limit_size != AF_INET) - fam_to_limit_size = AF_INET6; + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { @@ -225,12 +229,17 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); - if (transmit_chunk_size > size_limit) - throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); + if (transmit_chunk_size > max_payload_size) + Error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assign the others here. m_options = par; @@ -294,6 +303,21 @@ bool SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + // we do one client connection at a time, // so close the listener. srt_close(m_bindsock); @@ -462,6 +486,19 @@ void SrtCommon::ConnectClient(string host, int port) Error("srt_connect"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + stat = ConfigurePost(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePost"); diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 527f005d9..a4812d815 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -42,6 +42,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; SRTSOCKET m_sock = SRT_INVALID_SOCK; SRTSOCKET m_bindsock = SRT_INVALID_SOCK; bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5646e3e79..371bf1b0a 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4331,6 +4331,47 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) } } +int srt::CUDT::getMaxPayloadSize(SRTSOCKET id) +{ + return uglobal().getMaxPayloadSize(id); +} + +int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) +{ + CUDTSocket* s = locateSocket(id); + if (!s) + { + return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL); + } + + if (s->m_SelfAddr.family() == AF_UNSPEC) + { + return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND); + } + + int fam = s->m_SelfAddr.family(); + CUDT& u = s->core(); + + std::string errmsg; + int extra = u.m_config.extraPayloadReserve((errmsg)); + if (extra == -1) + { + LOGP(aclog.Error, errmsg); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL); + } + + // Prefer transfer IP version, if defined. This is defined after + // the connection is established. Note that the call is rejected + // if the socket isn't bound, be it explicitly or implicitly by + // calling srt_connect(). + if (u.m_TransferIPVersion != AF_UNSPEC) + fam = u.m_TransferIPVersion; + + int payload_size = u.m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(fam) - extra; + + return payload_size; +} + //////////////////////////////////////////////////////////////////////////////// namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..6a5783044 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -412,6 +412,8 @@ class CUDTUnited CUDTSocket* locateSocket_LOCKED(SRTSOCKET u); CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn); + int getMaxPayloadSize(SRTSOCKET u); + #if ENABLE_BONDING CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); CUDTGroup* acquireSocketsGroup(CUDTSocket* s); diff --git a/srtcore/core.h b/srtcore/core.h index 6945e4276..4d3a84f42 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -245,6 +245,7 @@ class CUDT static int rejectReason(SRTSOCKET s); static int rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); + static int getMaxPayloadSize(SRTSOCKET u); public: // internal API // This is public so that it can be used directly in API implementation functions. diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index de6c8948b..ca75df277 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -1037,8 +1037,10 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } -bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW { + int resv = 0; + if (!this->sPacketFilterConfig.empty()) { // This means that the filter might have been installed before, @@ -1048,29 +1050,41 @@ bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errms if (!ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) { // Break silently. This should not happen - w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; - return false; + w_info = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return -1; } - const size_t efc_max_payload_size = CPacket::srtPayloadSize(ip_family) - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) << " bytes decreased by " << fc.extra_size - << " required for packet filter header"; - w_errmsg = log.str(); - return false; - } + resv += fc.extra_size; + w_info = "Packet Filter"; } - // Not checking AUTO to allow defaul 1456 bytes. - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (CPacket::srtPayloadSize(ip_family) - HAICRYPT_AUTHTAG_MAX))) + if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)) + { + resv += HAICRYPT_AUTHTAG_MAX; + if (!w_info.empty()) + w_info += " and "; + w_info += "AES_GCM mode"; + } + + return resv; +} + +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + int resv = extraPayloadReserve((w_errmsg)); + if (resv == -1) + return false; + + size_t valmax = CPacket::srtPayloadSize(ip_family) - resv; + + if (val > valmax) { std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) - << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."; + log << "SRTO_PAYLOADSIZE: value " << val << "exceeds " << valmax + << " bytes"; + if (!w_errmsg.empty()) + log << " as limited by " << w_errmsg; + w_errmsg = log.str(); return false; } diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 20242512d..a04c782ef 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -348,6 +348,8 @@ struct CSrtConfig: CSrtMuxerConfig // This function returns the number of bytes that are allocated // for a single packet in the sender and receiver buffer. int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } + + int extraPayloadReserve(std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index c29e2c506..7e72238ba 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -312,9 +312,6 @@ SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) -// A macro for these above in case when the IP family is passed as a runtime value. -#define SRT_MAX_PLSIZE(famspec) ((famspec) == AF_INET ? SRT_MAX_PLSIZE_AF_INET : SRT_MAX_PLSIZE_AF_INET6) - // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; @@ -805,6 +802,9 @@ SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCK SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API int srt_getmaxpayloadsize(SRTSOCKET sock); + + typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; typedef struct SRT_MsgCtrl_ diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 031030cd7..8262b8ae0 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -169,6 +169,11 @@ int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } +int srt_getmaxpayloadsize(SRTSOCKET u) +{ + return CUDT::getMaxPayloadSize(u); +} + int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); } @@ -415,6 +420,9 @@ int srt_clock_type() return SRT_SYNC_CLOCK; } +// NOTE: crypto mode is defined regardless of the setting of +// ENABLE_AEAD_API_PREVIEW symbol. This can only block the symbol, +// but it doesn't change the symbol layout. const char* const srt_rejection_reason_msg [] = { "Unknown or erroneous", "Error in system calls", @@ -432,10 +440,9 @@ const char* const srt_rejection_reason_msg [] = { "Congestion controller type collision", "Packet Filter settings error", "Group settings collision", - "Connection timeout" -#ifdef ENABLE_AEAD_API_PREVIEW - ,"Crypto mode" -#endif + "Connection timeout", + "Crypto mode", + "Invalid configuration" }; // Deprecated, available in SRT API. @@ -456,14 +463,14 @@ extern const char* const srt_rejectreason_msg[] = { srt_rejection_reason_msg[13], srt_rejection_reason_msg[14], srt_rejection_reason_msg[15], - srt_rejection_reason_msg[16] -#ifdef ENABLE_AEAD_API_PREVIEW - , srt_rejection_reason_msg[17] -#endif + srt_rejection_reason_msg[16], + srt_rejection_reason_msg[17], + srt_rejection_reason_msg[18] }; const char* srt_rejectreason_str(int id) { + using namespace srt_logging; if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -471,7 +478,10 @@ const char* srt_rejectreason_str(int id) static const size_t ra_size = Size(srt_rejection_reason_msg); if (size_t(id) >= ra_size) + { + HLOGC(gglog.Error, log << "Invalid rejection code #" << id); return srt_rejection_reason_msg[0]; + } return srt_rejection_reason_msg[id]; } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 05dd39330..b7344425e 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -393,7 +393,7 @@ void SrtCommon::InitParameters(string host, string path, map par) m_mode = par.at("mode"); } - int fam_to_limit_size = AF_INET6; // take the less one as default + size_t max_payload_size = 0; // Try to interpret host and adapter first sockaddr_any host_sa, adapter_sa; @@ -401,27 +401,31 @@ void SrtCommon::InitParameters(string host, string path, map par) if (host != "") { host_sa = CreateAddr(host); - fam_to_limit_size = host_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (host_sa.family() == AF_UNSPEC) Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } if (adapter != "") { adapter_sa = CreateAddr(adapter); - fam_to_limit_size = adapter_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (adapter_sa.family() == AF_UNSPEC) Error("Failed to interpret 'adapter' spec: " + adapter); if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) { Error("Both host and adapter specified and they use different IP versions"); } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } - if (fam_to_limit_size != AF_INET) - fam_to_limit_size = AF_INET6; + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) @@ -479,12 +483,17 @@ void SrtCommon::InitParameters(string host, string path, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); - if (transmit_chunk_size > size_limit) - throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); + if (transmit_chunk_size > max_payload_size) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assigning group configuration from a special "groupconfig" attribute. // This is the only way how you can set up this configuration at the listener side. @@ -598,6 +607,21 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + #if ENABLE_BONDING if (m_sock & SRTGROUP_MASK) { @@ -1435,6 +1459,19 @@ void SrtCommon::ConnectClient(string host, int port) transmit_error_storage.clear(); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + Verb() << " connected."; stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..d40d63dee 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -104,6 +104,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; vector m_group_nodes; string m_group_type; string m_group_config; From 49346a218b6f8e6739f8e1fdb4364142c242a476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 18 Oct 2023 13:47:00 +0200 Subject: [PATCH 078/174] Removed BytesPacketsCount. Shifted header size to a function parameter --- srtcore/core.cpp | 50 +++++++++++++++++------------------- srtcore/core.h | 6 ----- srtcore/group.cpp | 18 ++++++++----- srtcore/stats.h | 65 ++++++++--------------------------------------- 4 files changed, 46 insertions(+), 93 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 22876663f..765ffd2de 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4715,7 +4715,6 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); - m_stats.setupHeaderSize(full_hdr_size); m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; @@ -5571,7 +5570,7 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } return iDropCnt; @@ -5595,7 +5594,7 @@ void srt::CUDT::setInitialRcvSeq(int32_t isn) const int iDropCnt = m_pRcvBuffer->dropAll(); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); sync::ScopedLock sl(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } m_pRcvBuffer->setStartSeqNo(isn); @@ -5738,7 +5737,6 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); - m_stats.setupHeaderSize(full_hdr_size); // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; @@ -7452,17 +7450,17 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvFilterLoss = m_stats.rcvr.lossFilter.trace.count(); /* perf byte counters include all headers (SRT+UDP+IP) */ - 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.lost.trace.bytesWithHdr(); + perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr(pktHdrSize); + perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr(pktHdrSize); + perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr(pktHdrSize); + perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr(pktHdrSize); + perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvLoss = m_stats.rcvr.lost.trace.bytesWithHdr(pktHdrSize); perf->pktSndDrop = m_stats.sndr.dropped.trace.count(); perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count(); - perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(); - perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(); + perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(pktHdrSize); perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count(); perf->byteRcvUndecrypt = m_stats.rcvr.undecrypted.trace.bytes(); @@ -7479,22 +7477,22 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRecvNAKTotal = m_stats.sndr.recvdNak.total.count(); perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - 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->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr(pktHdrSize); + perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr(pktHdrSize); + perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr(pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr(pktHdrSize); + perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr(pktHdrSize); 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->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(pktHdrSize); perf->pktSndDropTotal = m_stats.sndr.dropped.total.count(); perf->pktRcvDropTotal = m_stats.rcvr.dropped.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(); + perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(pktHdrSize); + perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr(pktHdrSize); perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); @@ -9005,7 +9003,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } } @@ -10122,8 +10120,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10139,8 +10137,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt* rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt* rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Packet not encrypted (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10340,7 +10338,7 @@ int srt::CUDT::processData(CUnit* in_unit) ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.lost.count(stats::BytesPacketsCount(loss * avgpayloadsz, (uint32_t) loss)); + m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " diff --git a/srtcore/core.h b/srtcore/core.h index 4d3a84f42..41042ff50 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1165,12 +1165,6 @@ class CUDT int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration - void setupHeaderSize(int hsize) - { - sndr.setupHeaderSize(hsize); - rcvr.setupHeaderSize(hsize); - } - } m_stats; public: diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 02ad7b759..934550847 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2379,6 +2379,12 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) const steady_clock::time_point currtime = steady_clock::now(); + // NOTE: Potentially in the group we might be using both IPv4 and IPv6 + // links and sending a single packet over these two links could be different. + // These stats then don't make much sense in this form, this has to be + // redesigned. We use the header size as per IPv4, as it was everywhere. + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(AF_INET); + memset(perf, 0, sizeof *perf); ScopedLock gg(m_GroupLock); @@ -2389,17 +2395,17 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) perf->pktRecvUnique = m_stats.recv.trace.count(); perf->pktRcvDrop = m_stats.recvDrop.trace.count(); - perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(); - perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(); - perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(); + perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(pktHdrSize); + perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(pktHdrSize); 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.bytesWithHdr(); - perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(); - perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(); + perf->byteSentUniqueTotal = m_stats.sent.total.bytesWithHdr(pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(pktHdrSize); + perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(pktHdrSize); const double interval = static_cast(count_microseconds(currtime - m_stats.tsLastSampleTime)); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; diff --git a/srtcore/stats.h b/srtcore/stats.h index 55d8d00d9..947489eb1 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -22,8 +22,6 @@ namespace stats class Packets { public: - typedef Packets count_type; - Packets() : m_count(0) {} Packets(uint32_t num) : m_count(num) {} @@ -48,15 +46,15 @@ class Packets uint32_t m_count; }; -class BytesPacketsCount +class BytesPackets { public: - BytesPacketsCount() + BytesPackets() : m_bytes(0) , m_packets(0) {} - BytesPacketsCount(uint64_t bytes, uint32_t n = 1) + BytesPackets(uint64_t bytes, uint32_t n = 1) : m_bytes(bytes) , m_packets(n) {} @@ -84,43 +82,23 @@ class BytesPacketsCount return m_packets; } - BytesPacketsCount& operator+= (const BytesPacketsCount& other) + BytesPackets& operator+= (const BytesPackets& other) { m_bytes += other.m_bytes; m_packets += other.m_packets; return *this; } + uint64_t bytesWithHdr(size_t hdr_size) const + { + return m_bytes + m_packets * hdr_size; + } + protected: uint64_t m_bytes; uint32_t m_packets; }; -class BytesPackets: public BytesPacketsCount -{ -public: - typedef BytesPacketsCount count_type; - - // Set IPv4-based header size value as a fallback. This will be fixed upon connection. - BytesPackets() - : m_zPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) - {} - -public: - - void setupHeaderSize(int size) - { - m_zPacketHeaderSize = uint64_t(size); - } - - uint64_t bytesWithHdr() const - { - return m_bytes + m_packets * m_zPacketHeaderSize; - } - -private: - uint64_t m_zPacketHeaderSize; -}; template struct Metric @@ -128,7 +106,7 @@ struct Metric METRIC_TYPE trace; METRIC_TYPE total; - void count(typename METRIC_TYPE::count_type val) + void count(BASE_METRIC_TYPE val) { trace += val; total += val; @@ -166,16 +144,6 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - void setupHeaderSize(int hdr_size) - { -#define SETHSIZE(var) var.setupHeaderSize(hdr_size) - SETHSIZE(sent); - SETHSIZE(sentUnique); - SETHSIZE(sentRetrans); - SETHSIZE(dropped); -#undef SETHSIZE - } - void reset() { sent.reset(); @@ -219,19 +187,6 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - void setupHeaderSize(int hdr_size) - { -#define SETHSIZE(var) var.setupHeaderSize(hdr_size) - SETHSIZE(recvd); - SETHSIZE(recvdUnique); - SETHSIZE(recvdRetrans); - SETHSIZE(lost); - SETHSIZE(dropped); - SETHSIZE(recvdBelated); - SETHSIZE(undecrypted); -#undef SETHSIZE - } - void reset() { recvd.reset(); From e1262cc99d4733972abdd8b76d5fc61c43c72540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 19 Oct 2023 13:32:24 +0200 Subject: [PATCH 079/174] Added documentation for srt_getmaxpayloadsize --- docs/API/API-functions.md | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index f4a7aa07b..8ae4e96d4 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -19,6 +19,7 @@ | [srt_bind_acquire](#srt_bind_acquire) | Acquires a given UDP socket instead of creating one | | [srt_getsockstate](#srt_getsockstate) | Gets the current status of the socket | | [srt_getsndbuffer](#srt_getsndbuffer) | Retrieves information about the sender buffer | +| [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) | Retrieves the information about the maximum payload size in a single packet | | [srt_close](#srt_close) | Closes the socket or group and frees all used resources | | | | @@ -293,6 +294,7 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne * [srt_bind_acquire](#srt_bind_acquire) * [srt_getsockstate](#srt_getsockstate) * [srt_getsndbuffer](#srt_getsndbuffer) +* [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) * [srt_close](#srt_close) @@ -540,6 +542,58 @@ 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) + +--- + +### srt_getmaxpayloadsize + +``` +int srt_getmaxpayloadsize(SRTSOCKET u); +``` + +Returns the maximum number of bytes that fit in a single packet. Useful only in +live mode (when `SRTO_TSBPDMODE` is true). The socket must be bound (see +[srt_bind](#srt_bind)) or connected (see [srt_connect](#srt_connect)) +to use this function. Note that in case when the socket is bound to an IPv6 +wildcard address and it is dual-stack (`SRTO_IPV6ONLY` is set to false), this +function returns the correct value only if the socket is connected, otherwise +it will return the value always as if the connection was made from an IPv6 peer +(including when you call it on a listening socket). + +This function is only useful for the application to check if it is able to use +a payload of certain size in the live mode, or after connection, if the application +can send payloads of certain size. This is useful only in assertions, as if the +[`SRTO_PAYLOADSIZE`](API_socket-options.md#SRTO_PAYLOADSIZE) option is to be +set to a non-default value (for which the one returned by this function is the +maximum value), this option should be modified before connection and on both +parties, regarding the settings applied on the socket. + +The returned value is the maximum number of bytes that can be put in a single +packet regarding: + +* The current MTU size (`SRTO_MSS`) +* The IP version (IPv4 or IPv6) +* The `SRTO_CRYPTOMODE` setting (bytes reserved for AEAD authentication tag) +* The `SRTO_PACKETFILTER` setting (bytes reserved for extra field in a FEC control packet) + +With default options this value should be 1456 for IPv4 and 1444 for IPv6. + + +| Returns | | +|:----------------------------- |:------------------------------------------------- | +| The maximum payload size (>0) | If succeeded | +| `SRT_ERROR` | Usage error | +| | | + +| Errors | | +|:--------------------------------------- |:----------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | Socket [`u`](#u) is not bound | +| | | + + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- From f91f75a9619a1cd7496818527fd2fa8231c18ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 16:00:16 +0100 Subject: [PATCH 080/174] Added group contents check and configurable sleep for group/listener tests --- test/test_epoll.cpp | 91 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 38624a62e..2c98ec38d 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -548,6 +548,9 @@ TEST(CEPoll, ThreadedUpdate) void testListenerReady(const bool LATE_CALL, size_t nmembers) { + bool is_single = true; + bool want_sleep = !TestEnv::me->OptionPresent("nosleep"); + sockaddr_in sa; memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; @@ -564,6 +567,7 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); int on = 1; EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + is_single = false; } else { @@ -588,7 +592,8 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // We don't need the caller to be async, it can hang up here. for (size_t i = 0; i < nmembers; ++i) { - connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); })); } @@ -596,8 +601,11 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) std::cout << "STARTED connecting...\n"; } - std::cout << "Sleeping 1s...\n"; - this_thread::sleep_for(chrono::milliseconds(1000)); + if (want_sleep) + { + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + } // What is important is that the accepted socket is now reporting in // on the listener socket. So let's create an epoll. @@ -616,7 +624,8 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // We don't need the caller to be async, it can hang up here. for (size_t i = 0; i < nmembers; ++i) { - connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); })); } @@ -640,8 +649,9 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) std::cout << "With >1 members, check if there's still UPDATE pending\n"; // Spawn yet another connection within the group, just to get the update auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + std::cout << "[T:X] CALLING (expected failure)\n"; + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); // For 2+ members, additionally check if there AREN'T any // further acceptance members, but there are UPDATEs. EXPECT_EQ(srt_epoll_uwait(eid_postcheck, fdset, 1, 5000), 1); @@ -649,15 +659,80 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // SUBSCRIBED EVENTS: IN, UPDATE. // expected: UPDATE only. EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); - EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + SRTSOCKET joined = extra_call.get(); + EXPECT_NE(joined, SRT_INVALID_SOCK); + std::cout << Sprint("Extra joined: @", joined, "\n"); + } + + std::vector gdata; + + if (!is_single) + { + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Listener) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); } std::cout << "Joining connector thread(s)\n"; for (size_t i = 0; i < nmembers; ++i) { - EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + std::cout << "Join: #" << i << ":\n"; + SRTSOCKET called_socket = connect_res[i].get(); + std::cout << "... " << called_socket << std::endl; + EXPECT_NE(called_socket, SRT_INVALID_SOCK); } + if (!is_single) + { + EXPECT_EQ(caller_sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(caller_sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Caller) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); + + if (want_sleep) + { + std::cout << "Sleep for 3 seconds to avoid closing-in-between\n"; + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + } + + std::cout << "Releasing EID resources and all sockets\n"; + srt_epoll_release(eid); srt_epoll_release(eid_postcheck); From 8072420ad762feb1c67cd1f8c3b7da1775ae18a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 16:50:42 +0100 Subject: [PATCH 081/174] Fixed warn-error build break on mac --- srtcore/socketconfig.cpp | 2 +- testing/srt-test-live.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index ca75df277..446d9b201 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -1058,7 +1058,7 @@ int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW w_info = "Packet Filter"; } - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)) + if (this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) { resv += HAICRYPT_AUTHTAG_MAX; if (!w_info.empty()) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..aa6b45d6a 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 188cbd1b4b2a43c50792b4a37e1a23dd447d6c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 17:15:56 +0100 Subject: [PATCH 082/174] Fixed build break on more pedantic compilers --- test/test_epoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 2c98ec38d..c720a1075 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -823,7 +823,7 @@ void testMultipleListenerReady(const bool LATE_CALL) connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { this_thread::sleep_for(chrono::milliseconds(1)); - return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); })); @@ -856,7 +856,7 @@ void testMultipleListenerReady(const bool LATE_CALL) connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { this_thread::sleep_for(chrono::milliseconds(1)); - return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); })); std::cout << "STARTED connecting...\n"; From e8cd37d25d45881da518ae794e01be55e7612378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 23 Jan 2024 20:05:12 +0100 Subject: [PATCH 083/174] Further fixes for OptionProxy. Some cosmetics in the docs --- apps/apputil.hpp | 31 +++++++++++++++++++++++++------ docs/API/API-functions.md | 6 ++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index c79cf984e..1a0b158e0 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -350,22 +350,41 @@ struct OptionSetterProxy struct OptionProxy { - const OptionSetterProxy& parent; + OptionSetterProxy& parent; SRT_SOCKOPT opt; - template - OptionProxy& operator=(Type&& val) +#define SPEC(type) \ + OptionProxy& operator=(const type& val)\ + {\ + parent.result = srt_setsockflag(parent.s, opt, &val, sizeof val);\ + return *this;\ + } + + SPEC(int32_t); + SPEC(int64_t); + SPEC(bool); +#undef SPEC + + template + OptionProxy& operator=(const char (&val)[N]) + { + parent.result = srt_setsockflag(parent.s, opt, val, N-1); + return *this; + } + + OptionProxy& operator=(const std::string& val) { - Type vc(val); - srt_setsockflag(parent.s, opt, &vc, sizeof vc); + parent.result = srt_setsockflag(parent.s, opt, val.c_str(), val.size()); return *this; } }; - OptionProxy operator[](SRT_SOCKOPT opt) const + OptionProxy operator[](SRT_SOCKOPT opt) { return OptionProxy {*this, opt}; } + + operator int() { return result; } }; inline OptionSetterProxy setopt(SRTSOCKET socket) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 7608d32f0..368216005 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -639,7 +639,7 @@ below). * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of the returned object -General requirements for parameter correctness: +General requirements for a parameter correctness: * `lsn` must be first [bound](#srt_bind) and [listening](#srt_listen) @@ -656,7 +656,7 @@ depends on the address type used in the `srt_bind` call for `lsn`. If unsure in a particular situation, it is recommended that you use `sockaddr_storage` or `srt::sockaddr_any`. -If the `lsn` listener socket is blocking mode (if +If the `lsn` listener socket is in the blocking mode (if [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, which is default), the call will block until the incoming connection is ready for extraction. Otherwise, the call always returns immediately, possibly with @@ -707,6 +707,8 @@ request. This feature is prone to more tricky rules, however: continue blocking. If you want to use only one thread for accepting connections from potentially multiple listening sockets in the blocking mode, you should use [`srt_accept_bond`](#srt_accept_bond) instead. + Note though that this function is actually a wrapper that changes locally + to the nonblocking mode on all these listeners and uses epoll internally. * If at the moment multiple listener sockets have received connection request and you query them all for readiness epoll flags (by calling From 8ad69ed81243466e633bc02960ef0f87cb786da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 15 Feb 2024 13:38:20 +0100 Subject: [PATCH 084/174] Fixed a suggested uninitialized variable --- srtcore/buffer_rcv.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 488b73f35..235909e90 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -364,6 +364,11 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iDropPos); avail_range = 1; } + else + { + avail_packet = NULL; + avail_range = 0; + } IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); From bbced79792597a1d11f9051be9ac157658878fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 09:19:47 +0100 Subject: [PATCH 085/174] Renamed eclipsed variable --- srtcore/buffer_rcv.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 235909e90..a7693a839 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -1495,7 +1495,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - int pos = incPos(m_iStartPos, offset); + int frompos = incPos(m_iStartPos, offset); // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1506,7 +1506,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) int ret_off = m_iMaxPosOff; int end_off = offPos(m_iStartPos, m_iEndPos); - if (pos < end_off) + if (frompos < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. @@ -1560,7 +1560,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the From 5136ca2c3eb6ffb8c6c92fe55cc154cb2513151d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 10:32:39 +0100 Subject: [PATCH 086/174] Added doxy description for some functions. Applied NonOrder in names for out-of-order concerns --- srtcore/buffer_rcv.cpp | 100 ++++++++++++++++++----------------------- srtcore/buffer_rcv.h | 59 +++++++++++++++++------- srtcore/utilities.h | 2 +- 3 files changed, 89 insertions(+), 72 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a7693a839..831840967 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -126,8 +126,8 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) - , m_numRandomPackets(0) - , m_iFirstRandomMsgPos(-1) + , m_numNonOrderPackets(0) + , m_iFirstNonOrderMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -337,8 +337,8 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numRandomPackets; - onInsertNotInOrderPacket(newpktpos); + ++m_numNonOrderPackets; + onInsertNonOrderPacket(newpktpos); } updateNonreadPos(); @@ -350,13 +350,13 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iStartPos); avail_range = offPos(m_iStartPos, m_iEndPos); } - else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) { // In case when TSBPD is off, we take into account the message mode // where messages may potentially span for multiple packets, therefore // the only "next deliverable" is the first complete message that satisfies // the order requirement. - avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_packet = &packetAt(m_iFirstNonOrderMsgPos); avail_range = 1; } else if (m_iDropPos != m_iEndPos) @@ -379,18 +379,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } -// This function should be called after having m_iEndPos -// has somehow be set to position of a non-empty cell. -// This can happen by two reasons: -// - the cell has been filled by incoming packet -// - the value has been reset due to shifted m_iStartPos -// This means that you have to search for a new gap and -// update the m_iEndPos and m_iDropPos fields, or set them -// both to the end of range. -// -// prev_max_pos should be the position represented by m_iMaxPosOff. -// Passed because it is already calculated in insert(), otherwise -// it would have to be calculated here again. void CRcvBuffer::updateGapInfo(int prev_max_pos) { int pos = m_iEndPos; @@ -474,7 +462,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) updateNonreadPos(); } if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableRandom(); + updateFirstReadableNonOrder(); IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; @@ -613,9 +601,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableRandom()) - m_iFirstRandomMsgPos = -1; - updateFirstReadableRandom(); + if (!checkFirstReadableNonOrder()) + m_iFirstNonOrderMsgPos = -1; + updateFirstReadableNonOrder(); } IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); @@ -645,14 +633,14 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstRandomMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -696,8 +684,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair 0 && m_iFirstRandomMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const @@ -1028,8 +1016,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } if (!haveInorderPackets) @@ -1053,11 +1041,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - if (m_iFirstRandomMsgPos >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstNonOrderMsgPos >= 0) { - SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = packetAt(m_iFirstRandomMsgPos); + SRT_ASSERT(m_numNonOrderPackets > 0); + const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1107,9 +1095,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { - --m_numRandomPackets; - if (pos == m_iFirstRandomMsgPos) - m_iFirstRandomMsgPos = -1; + --m_numNonOrderPackets; + if (pos == m_iFirstNonOrderMsgPos) + m_iFirstNonOrderMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -1185,9 +1173,9 @@ int CRcvBuffer::findLastMessagePkt() return -1; } -void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(int insertPos) { - if (m_numRandomPackets == 0) + if (m_numNonOrderPackets == 0) return; // If the following condition is true, there is already a packet, @@ -1196,7 +1184,7 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstRandomMsgPos >= 0) + if (m_iFirstNonOrderMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. @@ -1209,34 +1197,34 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstRandomMsgPos = insertPos; + // m_iFirstNonOrderMsgPos = insertPos; // return; //} const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); if (!hasLast) return; const int firstPktPos = (boundary & PB_FIRST) ? insertPos - : scanNotInOrderMessageLeft(insertPos, msgNo); + : scanNonOrderMessageLeft(insertPos, msgNo); if (firstPktPos < 0) return; - m_iFirstRandomMsgPos = firstPktPos; + m_iFirstNonOrderMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableRandom() +bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) return false; const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1257,16 +1245,16 @@ bool CRcvBuffer::checkFirstReadableRandom() return false; } -void CRcvBuffer::updateFirstReadableRandom() +void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) return; if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numRandomPackets; + int outOfOrderPktsRemain = (int) m_numNonOrderPackets; // Search further packets to the right. // First check if there are packets to the right. @@ -1309,7 +1297,7 @@ void CRcvBuffer::updateFirstReadableRandom() if (boundary & PB_LAST) { - m_iFirstRandomMsgPos = posFirst; + m_iFirstNonOrderMsgPos = posFirst; return; } @@ -1320,7 +1308,7 @@ void CRcvBuffer::updateFirstReadableRandom() return; } -int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. @@ -1351,7 +1339,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const return -1; } -int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 22393dbcd..70099d355 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -244,16 +244,45 @@ class CRcvBuffer }; - /// Insert a unit into the buffer. - /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + /// Inserts the unit with the data packet into the receiver buffer. + /// The result inform about the situation with the packet attempted + /// to be inserted and the readability of the buffer. /// - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. + /// @param [PASS] unit The unit that should be placed in the buffer + /// + /// @return The InsertInfo structure where: + /// * result: the result of insertion, which is: + /// * INSERTED: successfully placed in the buffer + /// * REDUNDANT: not placed, the packet is already there + /// * BELATED: not placed, its sequence is in the past + /// * DISCREPANCY: not placed, the sequence is far future or OOTB + /// * first_seq: the earliest sequence number now avail for reading + /// * avail_range: how many packets are available for reading (1 if unknown) + /// * first_time: the play time of the earliest read-available packet + /// If there is no available packet for reading, first_seq == SRT_SEQNO_NONE. /// - /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. - /// -3 if a packet is offset is ahead the buffer capacity. - // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. InsertInfo insert(CUnit* unit); + + /// Update the values of `m_iEndPos` and `m_iDropPos` in + /// case when `m_iEndPos` was updated to a position of a + /// nonempty cell. + /// + /// This function should be called after having m_iEndPos + /// has somehow be set to position of a non-empty cell. + /// This can happen by two reasons: + /// + /// - the cell has been filled by incoming packet + /// - the value has been reset due to shifted m_iStartPos + /// + /// This means that you have to search for a new gap and + /// update the m_iEndPos and m_iDropPos fields, or set them + /// both to the end of range if there are no loss gaps. + /// + /// The @a prev_max_pos parameter is passed here because it is already + /// calculated in insert(), otherwise it would have to be calculated here again. + /// + /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` + /// void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). @@ -495,12 +524,12 @@ class CRcvBuffer int findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstRandomMsgPos is still readable. - bool checkFirstReadableRandom(); - void updateFirstReadableRandom(); - int scanNotInOrderMessageRight(int startPos, int msgNo) const; - int scanNotInOrderMessageLeft(int startPos, int msgNo) const; + void onInsertNonOrderPacket(int insertpos); + // Check if m_iFirstNonOrderMsgPos is still readable. + bool checkFirstReadableNonOrder(); + void updateFirstReadableNonOrder(); + int scanNonOrderMessageRight(int startPos, int msgNo) const; + int scanNonOrderMessageLeft(int startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -566,12 +595,12 @@ class CRcvBuffer int m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) - size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false + size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstRandomMsgPos; + int m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 258a2fdc8..3d9cf1c09 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () { return 0!= get(); } + operator bool () const { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From f6e0271c72a16dd12942651eae4cec28a39f586c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 11:28:24 +0100 Subject: [PATCH 087/174] Removed wrong fix. Fixed C++11 style initialization of PacketInfo --- srtcore/buffer_rcv.cpp | 26 ++++++++++++++++++++------ srtcore/utilities.h | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 831840967..566854984 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -986,20 +986,34 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - // Check the state of the very first packet first + // Default: no packet available. + PacketInfo pi = { SRT_SEQNO_NONE, false, time_point() }; + + const CPacket* pkt = NULL; + + // Very first packet available with no gap. if (m_entries[m_iStartPos].status == EntryState_Avail) { SRT_ASSERT(m_entries[m_iStartPos].pUnit); - return { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - if (m_iDropPos != m_iEndPos) + else if (m_iDropPos != m_iEndPos) + { + SRT_ASSERT(m_entries[m_iDropPos].pUnit); + pkt = &packetAt(m_iDropPos); + pi.seq_gap = true; // Available, but after a drop. + } + else { - const CPacket& pkt = packetAt(m_iDropPos); - return { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; + // If none of them point to a valid packet, + // there is no packet available; + return pi; } - return { SRT_SEQNO_NONE, false, time_point() }; + pi.seqno = pkt->getSeqNo(); + pi.tsbpd_time = getPktTsbPdTime(pkt->getMsgTimeStamp()); + return pi; } std::pair CRcvBuffer::getAvailablePacketsRange() const diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 3d9cf1c09..258a2fdc8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () const { return 0!= get(); } + operator bool () { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From 072a8c482ec9a8963a9ca0efa0a4ac2ff0df9688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 16:29:55 +0100 Subject: [PATCH 088/174] Refax: CRcvBuffer extracted some parts of insert() to separate functions --- srtcore/buffer_rcv.cpp | 305 ++++++++++++++++++++++------------------- srtcore/buffer_rcv.h | 6 + 2 files changed, 167 insertions(+), 144 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 566854984..97b76d755 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -144,7 +144,7 @@ CRcvBuffer::~CRcvBuffer() { if (!it->pUnit) continue; - + m_pUnitQueue->makeUnitFree(it->pUnit); it->pUnit = NULL; } @@ -167,9 +167,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - int32_t avail_seq; - int avail_range; - if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); @@ -181,31 +178,12 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - // Calculation done for the sake of possible discrepancy - // in order to inform the caller what to do. - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_seq = packetAt(m_iStartPos).getSeqNo(); - avail_range = m_iEndPos - m_iStartPos; - } - else if (m_iDropPos == m_iEndPos) - { - avail_seq = SRT_SEQNO_NONE; - avail_range = 0; - } - else - { - avail_seq = packetAt(m_iDropPos).getSeqNo(); - - // We don't know how many packets follow it exactly, - // but in this case it doesn't matter. We know that - // at least one is there. - avail_range = 1; - } + InsertInfo ireport (InsertInfo::DISCREPANCY); + getAvailInfo((ireport)); IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); - return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); + return ireport; } // TODO: Don't do assert here. Process this situation somehow. @@ -243,95 +221,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time; + time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); - int prev_max_pos = incPos(m_iStartPos, prev_max_off); - - // Update flags - // Case [A] - if (extended_end) - { - // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) - { - // THIS means that the new packet didn't CAUSE a gap - if (m_iMaxPosOff == prev_max_off + 1) - { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, - // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iDropPos = m_iEndPos; - } - else - { - // Otherwise we have a drop-after-gap candidate - // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - } - } - } - // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. - - // Here you can use prev_max_pos as the position represented - // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) - { - // Case [D]: inserted a packet at the first gap following the - // contiguous region. This makes a potential to extend the - // contiguous region and we need to find its end. - - // If insertion happened at the very first packet, it is the - // new earliest packet now. In any other situation under this - // condition there's some contiguous packet range preceding - // this position. - if (m_iEndPos == m_iStartPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - - updateGapInfo(prev_max_pos); - } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) - { - // Case [C]: the newly inserted packet precedes the - // previous earliest delivery position after drop, - // that is, there is now a "better" after-drop delivery - // candidate. - - // New position updated a valid packet on an earlier - // position than the drop position was before, although still - // following a gap. - // - // We know it because if the position has filled a gap following - // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a - // contiguous series of valid packets following a gap, hence - // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; - - // If there's an inserted packet BEFORE drop-pos (which makes it - // a new drop-pos), while the very first packet is absent (the - // below condition), it means we have a new earliest-available - // packet. Otherwise we would have only a newly updated drop - // position, but still following some earlier contiguous range - // of valid packets - so it's earlier than previous drop, but - // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - } - // OTHERWISE: case [D] in which nothing is to be updated. + InsertInfo ireport (InsertInfo::INSERTED); + ireport.first_time = earlier_time; // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). @@ -343,40 +236,164 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) updateNonreadPos(); - CPacket* avail_packet = NULL; - - if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_packet = &packetAt(m_iStartPos); - avail_range = offPos(m_iStartPos, m_iEndPos); - } - else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - avail_packet = &packetAt(m_iFirstNonOrderMsgPos); - avail_range = 1; - } - else if (m_iDropPos != m_iEndPos) - { - avail_packet = &packetAt(m_iDropPos); - avail_range = 1; - } - else - { - avail_packet = NULL; - avail_range = 0; - } + // This updates only the first_seq and avail_range fields. + getAvailInfo((ireport)); IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); - if (avail_packet) - return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); - else - return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) + return ireport; +} + +void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) +{ + int fallback_pos = -1; + if (!m_tsbpd.isEnabled()) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + // NOTE THAT this field can as well be -1 already. + fallback_pos = m_iFirstNonOrderMsgPos; + } + else if (m_iDropPos != m_iEndPos) + { + // With TSBPD regard the drop position (regardless if + // TLPKTDROP is currently on or off), if "exists", that + // is, m_iDropPos != m_iEndPos. + fallback_pos = m_iDropPos; + } + + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position (unless it's -1). + const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); + if (pkt) + { + w_if.first_seq = pkt->getSeqNo(); + } +} + + +const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +{ + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + pos = m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + } + + if (pos == -1) + { + w_span = 0; + return NULL; + } + + SRT_ASSERT(m_entries[pos].pUnit != NULL); + + // TODO: we know that at least 1 packet is available, but only + // with m_iEndPos we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_span = 1; + return &packetAt(pos); +} + +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +{ + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + + return earlier_time; } void CRcvBuffer::updateGapInfo(int prev_max_pos) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 70099d355..f25528832 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -106,6 +106,8 @@ namespace srt // [D] [C] [B] [A] (insertion cases) // | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | // +// See the CRcvBuffer::updatePosInfo method for detailed implementation. +// // WHEN INSERTING A NEW PACKET: // // If the incoming sequence maps to newpktpos that is: @@ -263,6 +265,10 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); + time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(int pos, int& w_span); + void getAvailInfo(InsertInfo& w_if); + /// Update the values of `m_iEndPos` and `m_iDropPos` in /// case when `m_iEndPos` was updated to a position of a /// nonempty cell. From d28e3b28f042235e627e7bd6b60c5611087b6c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 19 Feb 2024 16:44:39 +0100 Subject: [PATCH 089/174] Introduced SRTRUNSTATUS to cover all cases of srt_startup(). Fixed documentation. --- common/devel_util.h | 1 + docs/API/API-functions.md | 422 ++++++++++++++++++-------------------- srtcore/api.cpp | 14 +- srtcore/api.h | 2 +- srtcore/core.h | 2 +- srtcore/srt.h | 13 +- srtcore/srt_c_api.cpp | 2 +- srtcore/udt.h | 2 +- 8 files changed, 217 insertions(+), 241 deletions(-) diff --git a/common/devel_util.h b/common/devel_util.h index 3c00ec356..b908cedc6 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -84,6 +84,7 @@ struct IntWrapperLoose: IntWrapper typedef IntWrapper SRTSOCKET; typedef IntWrapper SRTSTATUS; +typedef IntWrapper SRTRUNSTATUS; typedef IntWrapperLoose SRTSTATUS_LOOSE; diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 54895fe15..a14d4aa19 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -222,6 +222,33 @@ Since SRT v1.5.0. | | | +## Diagnostics and return types + +The SRT API functions usually report a status of the operation that they attempt to perform. +There are three general possibilities to report a success or failure, possibly with some +extra information: + +1. `SRTSTATUS` is usually an integer value with two possible variants: + * `SRT_STATUS_OK` (value: 0): the operation completed successfully + * `SRT_ERROR` (value: -1): the operation failed (see [`srt_getlasterror`](#srt_getlasterror)) + +2. `SRTSOCKET` can be returned by some of the functions, which can be: + * A positive value greater than 0, which is a valid Socket ID value + * `SRT_SOCKID_CONNREQ` for a success report when a Socket ID needs not be returned + * `SRT_INVALID_SOCK` for a failure report + +3. An value of type `int` that should be a positive value or 0 in case of a success, +and the value equal to `SRT_ERROR` (that is, -1) in case of failure. + +In the below function description, functions returning `SRTSTATUS` will not +have the provided return value description, as it always maches the one above. +For all other types the function-specific return value description will be provided. + +If the function returns `SRT_ERROR`, `SRT_INVALID_SOCK` or a value equal to -1 +in case of returning an `int` value, additional error code can be obtained +through the [`srt_getlasterror`](#srt_getlasterror) call. Possible codes for a +particular function are listed in the **Errors** table. + ## Library Initialization @@ -231,7 +258,7 @@ Since SRT v1.5.0. ### srt_startup ``` -int srt_startup(void); +SRTRUNSTATUS srt_startup(void); ``` This function shall be called at the start of an application that uses the SRT @@ -242,10 +269,10 @@ relying on this behavior is strongly discouraged. | Returns | | |:----------------------------- |:--------------------------------------------------------------- | -| 0 | Successfully run, or already started | -| 1 | This is the first startup, but the GC thread is already running | -| -1 | Failed | -| | | +| `SRT_RUN_OK` (0) | Successfully started | +| `SRT_RUN_ALREADY` (1) | The GC thread is already running or it was called once already | +| `SRT_RUN_ERROR` (-1) | Failed | +| | | | Errors | | |:----------------------------- |:--------------------------------------------------------------- | @@ -259,7 +286,7 @@ relying on this behavior is strongly discouraged. ### srt_cleanup ``` -int srt_cleanup(void); +SRTSTATUS srt_cleanup(void); ``` This function cleans up all global SRT resources and shall be called just before @@ -267,10 +294,8 @@ exiting the application that uses the SRT library. This cleanup function will st be called from the C++ global destructor, if not called by the application, although relying on this behavior is strongly discouraged. -| Returns | | -|:----------------------------- |:--------------------------------------------------------------- | -| 0 | A possibility to return other values is reserved for future use | -| | | +Currently this function can only return `SRT_STATUS_OK` and a possibility to return +`SRT_ERROR` is reserved for future use. **IMPORTANT**: Note that the startup/cleanup calls have an instance counter. This means that if you call [`srt_startup`](#srt_startup) multiple times, you need to call the @@ -335,11 +360,11 @@ Note that socket IDs always have the `SRTGROUP_MASK` bit clear. |:----------------------------- |:------------------------------------------------------- | | Socket ID | A valid socket ID on success | | `SRT_INVALID_SOCK` | (`-1`) on error | -| | | +| | | -| Errors | | -|:----------------------------- |:------------------------------------------------------------ | -| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources . | +| Errors | | +|:----------------------------- |:-------------------------------------------------- | +| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources | | | | **NOTE:** This is probably a design flaw (:warning:   **BUG?**). Usually underlying system @@ -353,7 +378,7 @@ errors are reported by [`SRT_ECONNSETUP`](#srt_econnsetup). ### srt_bind ``` -int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Binds a socket to a local address and port. Binding specifies the local network @@ -361,10 +386,11 @@ interface and the UDP port number to be used for the socket. When the local address is a wildcard (`INADDR_ANY` for IPv4 or `in6addr_any` for IPv6), then it's bound to all interfaces. -**IMPORTANT**: When you bind an IPv6 wildcard address, note that the -`SRTO_IPV6ONLY` option must be set on the socket explicitly to 1 or 0 prior to -calling this function. See -[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) for more details. +**IMPORTANT**: In the case of IPv6 wildcard address, this may mean either "all +IPv6 interfaces" or "all IPv4 and IPv6 interfaces", depending on the value of +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option. Therefore this +option must be explicitly set to 0 or 1 prior to calling this function, otherwise +(when the default -1 value of this option is left) this function will fail. Binding is necessary for every socket to be used for communication. If the socket is to be used to initiate a connection to a listener socket, which can be done, @@ -411,7 +437,7 @@ binding ("shared binding") is possessed by an SRT socket created in the same application, and: * Its binding address and UDP-related socket options match the socket to be bound. -* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to *true* (default). +* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDR) is set to *true* (default). If none of the free, side and shared binding options is currently possible, this function will fail. If the socket blocking the requested endpoint is an SRT @@ -419,14 +445,15 @@ socket in the current application, it will report the `SRT_EBINDCONFLICT` error, while if it was another socket in the system, or the problem was in the system in general, it will report `SRT_ESOCKFAIL`. Here is the table that shows possible situations: -| Requested binding | vs. Existing bindings... | | | | | -|---------------------|------------------------------|-----------|-----------------------------|---------------|---------------| -| | A.B.C.D | 0.0.0.0 | ::X | :: / V6ONLY=1 | :: / V6ONLY=0 | -| 1.2.3.4 | 1.2.3.4 shareable, else free | blocked | free | free | blocked | -| 0.0.0.0 | blocked | shareable | free | free | blocked | -| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | -| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | -| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | +| Requested binding | vs. Existing bindings... | | | | | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| | A.B.C.D (explicit IPv4 addr.) | 0.0.0.0 | ::X (explicit IPv6 addr.) | :: / V6ONLY=1 | :: / V6ONLY=0 | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| 1.2.3.4 | shareable if 1.2.3.4, else free | blocked | free | free | blocked | +| 0.0.0.0 | blocked | shareable | free | free | blocked | +| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | +| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | +| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | Where: @@ -436,7 +463,7 @@ Where: * shareable: This binding can be shared with the requested binding if it's compatible. -* (ADDRESS) shareable, else free: this binding is shareable if the existing binding address is +* shareable if (ADDRESS), else free: this binding is shareable if the existing binding address is equal to the requested ADDRESS. Otherwise it's free. If the binding is shareable, then the operation will succeed if the socket that currently @@ -455,13 +482,9 @@ or set the appropriate source address using **IMPORTANT information about IPv6**: If you are going to bind to the `in6addr_any` IPv6 wildcard address (known as `::`), the `SRTO_IPV6ONLY` option must be first set explicitly to 0 or 1, otherwise the binding -will fail. In all other cases this option is meaningless. See `SRTO_IPV6ONLY` -option for more information. - -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) on error, otherwise 0 | -| | | +will fail. In all other cases this option is meaningless. See +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option for more +information. | Errors | | |:---------------------------------------- |:-------------------------------------------------------------------- | @@ -481,7 +504,7 @@ option for more information. ### srt_bind_acquire ``` -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); ``` A version of [`srt_bind`](#srt_bind) that acquires a given UDP socket instead of creating one. @@ -524,7 +547,7 @@ Gets the current status of the socket. Possible states are: ### srt_getsndbuffer ``` -int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); +SRTSTATUS srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); ``` Retrieves information about the sender buffer. @@ -546,18 +569,13 @@ socket needs to be closed asynchronously. ### srt_close ``` -int srt_close(SRTSOCKET u); +SRTSTATUS srt_close(SRTSOCKET u); ``` Closes the socket or group and frees all used resources. Note that underlying UDP sockets may be shared between sockets, so these are freed only with the last user closed. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------- |:----------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -585,7 +603,7 @@ last user closed. ### srt_listen ``` -int srt_listen(SRTSOCKET u, int backlog); +SRTSTATUS srt_listen(SRTSOCKET u, int backlog); ``` This sets up the listening state on a socket with a backlog setting that @@ -600,11 +618,6 @@ be called before [`srt_accept`](#srt_accept) can happen * [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) option allows the listener socket to accept group connections -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0. | -| | | - | Errors | | |:--------------------------------------- |:-------------------------------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | Value of `backlog` is 0 or negative. | @@ -721,7 +734,7 @@ calling this function. | Returns | | |:----------------------------- |:---------------------------------------------------------------------- | -| SRT socket
group ID | On success, a valid SRT socket or group ID to be used for transmission | +| SRT socket/group ID | On success, a valid SRT socket or group ID to be used for transmission | | `SRT_INVALID_SOCK` | (-1) on failure | | | | @@ -741,7 +754,7 @@ calling this function. ### srt_listen_callback ``` -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a socket that is @@ -755,12 +768,6 @@ connection has been accepted. * `hook_fn`: The callback hook function pointer (or NULL to remove the callback) * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:---------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - | Errors | | |:--------------------------------- |:----------------------------------------- | | [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| @@ -826,7 +833,7 @@ database you have to check against the data received in `streamid` or `peeraddr` ### srt_connect ``` -int srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Connects a socket or a group to a remote party with a specified address and port. @@ -844,8 +851,8 @@ Connects a socket or a group to a remote party with a specified address and port 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). +If not used, the binding will be done automatically to a wildcard address and port 0. See +[`srt_bind](#srt_bind) for details. 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 @@ -861,16 +868,21 @@ automatically for every call of this function. 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. +The returned value is a socket ID value. When `u` is a socket ID, the returned +is a special value `SRT_SOCKID_CONNREQ`. When `u` is a group ID, the returned +value is the socket ID of the newly created member for the requested link. In +the case of failure, `SRT_INVALID_SOCK` is returned. + | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | | Errors | | |:------------------------------------- |:----------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket or group ID | | [`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 | @@ -895,7 +907,9 @@ In the case of "late" failures you can additionally call 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. +can also be obtained from this function. Note that the connection timeout +error can be also recognized through this call, even though it is reported +by `SRT_ENOSERVER` in the blocking mode. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -905,7 +919,7 @@ can also be obtained from this function. ### srt_connect_bind ``` -int srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); ``` @@ -923,8 +937,8 @@ first on the automatically created socket for the connection. | Returns | | |:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | @@ -953,7 +967,7 @@ different families (that is, both `source` and `target` must be `AF_INET` or ### srt_connect_debug ``` -int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); ``` This function is for developers only and can be used for testing. It does the @@ -968,7 +982,7 @@ is generated randomly. ### srt_rendezvous ``` -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); ``` Performs a rendezvous connection. This is a shortcut for doing bind locally, @@ -981,11 +995,6 @@ to true, and doing [`srt_connect`](#srt_connect). * `local_name`: specifies the local network interface and port to bind * `remote_name`: specifies the remote party's IP address and port -| Returns | | -|:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------------- |:-------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | @@ -1007,7 +1016,7 @@ allowed (that is, both `local_name` and `remote_name` must be `AF_INET` or `AF_I ### srt_connect_callback ``` -int srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a given [`u`](#u) @@ -1036,16 +1045,10 @@ connection failures. * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - -| Errors | | -|:---------------------------------- |:------------------------------------------| -| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| -| | | +| Errors | | +|:---------------------------------- |:-------------------------------------------| +| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket | +| | | The callback function signature has the following type definition: @@ -1117,7 +1120,7 @@ where: * `token`: An integer value unique for every connection, or -1 if unused The `srt_prepare_endpoint` sets these fields to default values. After that -you can change the value of `weight` and `config` and `token` fields. The +you can change the value of `weight`, `config` and `token` fields. The `weight` parameter's meaning is dependent on the group type: * BROADCAST: not used @@ -1261,10 +1264,12 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRTSOCKET` | Corresponding group SRT socket ID of the member socket. | +| `SRTSOCKET` | Corresponding group SRT socket ID of the `member` socket. | | `SRT_INVALID_SOCK` | The socket doesn't exist, it is not a member of any group, or bonding API is disabled. | | | | +In the case of `SRT_INVALID_SOCK`, the error is set to `SRT_EINVPARAM`. + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1273,7 +1278,7 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb #### srt_group_data ``` -int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); +SRTSTATUS srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); ``` **Arguments**: @@ -1284,23 +1289,26 @@ int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* in and is set to the filled array's size This function obtains the current member state of the group specified in -`socketgroup`. The `output` should point to an array large enough to hold all -the elements. The `inoutlen` should point to a variable initially set to the size -of the `output` array. The current number of members will be written back to `inoutlen`. +`socketgroup`. -If the size of the `output` array is enough for the current number of members, -the `output` array will be filled with group data and the function will return -the number of elements filled. Otherwise the array will not be filled and -`SRT_ERROR` will be returned. +The `inoutlen` should point to a variable initially set to the size +of the `output` array. The current number of members will be written back to +the variable specified in `inoutlen`. This paramterer cannot be NULL. -This function can be used to get the group size by setting `output` to `NULL`, -and providing `socketgroup` and `inoutlen`. +If `output` is specified and the size of the array is at least equal to the +number of group members, the `output` array will be filled with group data. -| Returns | | -|:----------------------------- |:-------------------------------------------------- | -| # of elements | The number of data elements filled, on success | -| -1 | Error | -| | | +If `output` is NULL then the function will only retrieve the number of elements +in `inoutlen`. + +This call will fail and return `SRT_ERROR` if: + +* The `socketgroup` parameter is invalid + +* The `inoutlen` parameter is NULL + +* The size specified in a variable passed via `inoutlen` is less than the number +of group members | Errors | | @@ -1310,13 +1318,14 @@ and providing `socketgroup` and `inoutlen`. | | | -| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | -|:---------:|:--------------:|:------------:|:----------:|:------------:|:---------------------------------:| -| NULL | NULL | -1 | NULL | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| NULL | ptr | 0 | NULL | group.size() | ✖️ | -| ptr | NULL | -1 | ✖️ | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| ptr | ≥ group.size | group.size() | group.data | group.size | ✖️ | -| ptr | < group.size | -1 | ✖️ | group.size | [`SRT_ELARGEMSG`](#srt_elargemsg) | + +| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | +|:---------:|:--------------:|:---------------:|:----------:|:------------:|:---------------------------------:| +| ptr | ≥ group.size | `SRT_STATUS_OK` | group.data | group.size() | ✖️ | +| NULL | ptr | `SRT_STATUS_OK` | (unused) | group.size() | ✖️ | +| NULL | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | < group.size | `SRT_ERROR` | (unused) | group.size() | [`SRT_ELARGEMSG`](#srt_elargemsg) | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1326,14 +1335,14 @@ and providing `socketgroup` and `inoutlen`. #### srt_connect_group ``` -int srt_connect_group(SRTSOCKET group, - SRT_SOCKGROUPCONFIG name [], int arraysize); +SRTSOCKET srt_connect_group(SRTSOCKET group, + SRT_SOCKGROUPCONFIG links [], int arraysize); ``` This function does almost the same as calling [`srt_connect`](#srt_connect) or [`srt_connect_bind`](#srt_connect_bind) (when the source was specified for [`srt_prepare_endpoint`](#srt_prepare_endpoint)) in a loop for every item specified -in the `name` array. However if blocking mode is being used, the first call to +in the `links` array. However if blocking mode is being used, 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. @@ -1342,21 +1351,21 @@ option), there's no difference, except that the [`SRT_SOCKGROUPCONFIG`](#SRT_SOC structure allows adding extra configuration data used by groups. Note also that this function accepts only groups, not sockets. -The elements of the `name` array need to be prepared with the use of the +The elements of the `links` array need to be prepared with the use of the [`srt_prepare_endpoint`](#srt_prepare_endpoint) function. Note that it is **NOT** required that every target address specified is of the same family. Return value and errors in this function are the same as in [`srt_connect`](#srt_connect), although this function reports success when at least one connection has succeeded. If none has succeeded, this function reports an [`SRT_ECONNLOST`](#srt_econnlost) -error. Particular connection states can be obtained from the `name` -array upon return from the [`errorcode`](#error-codes) field. +error. Particular connection states can be obtained from the `links` array upon +return from the [`errorcode`](#error-codes) field. The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the following meaning: **Input**: -* `id`: unused, should be -1 (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) +* `id`: unused, should be `SRT_INVALID_SOCK` (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) * `srcaddr`: address to bind before connecting, if specified (see below for details) * `peeraddr`: target address to connect * `weight`: weight value to be set on the link @@ -1366,7 +1375,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f **Output**: -* `id`: The socket created for that connection (-1 if failed to create) +* `id`: The socket created for that connection (`SRT_INVALID_SOCK` if failed to create) * `srcaddr`: unchanged * `peeraddr`: unchanged * `weight`: unchanged @@ -1374,10 +1383,11 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f * [`errorcode`](#error-codes): status of connection for that link ([`SRT_SUCCESS`](#srt_success) if succeeded) * `token`: same as in input, or a newly created token value if input was -1 + | Returns | | |:----------------------------- |:-------------------------------------------------- | -| `SRT_SOCKET` | The socket ID of the first connected member. | -| -1 | Error | +| Socket ID | The socket ID of the first connected member. | +| `SRT_INVALID_SOCK` | Error | | | | @@ -1388,7 +1398,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f | | | The procedure of connecting for every connection definition specified -in the `name` array is performed the following way: +in the `links` array is performed the following way: 1. The socket for this connection is first created @@ -1415,7 +1425,8 @@ then, and for which the connection attempt has at least successfully started, remain group members, although the function will return immediately with an error status (that is, without waiting for the first successful connection). If your application wants to do any partial recovery from this situation, it can -only use the epoll mechanism to wait for readiness. +only check the current member status via [`srt_group_data`](#srt_group_data) +and wait for group's write readiness (`SRT_EPOLL_OUT`) by using epoll. 2. In any other case, if an error occurs at any stage of the above process, the processing is interrupted for this very array item only, the socket used for it @@ -1423,16 +1434,18 @@ is immediately closed, and the processing of the next elements continues. In the of a connection process, it also passes two stages - parameter check and the process itself. Failure at the parameter check breaks this process, while if the check passes, this item is considered correctly processed, even if the connection -attempt is going to fail later. If this function is called in blocking mode, -it then blocks until at least one connection reports success, or if all of them -fail. The status of connections that continue in the background after this function -exits can then be checked by [`srt_group_data`](#srt_group_data). +attempt is going to fail later. + +If this function is called in blocking mode, it then blocks until at least one +connection reports success, or if all of them fail. The status of connections +that continue in the background after this function exits can then be checked +by [`srt_group_data`](#srt_group_data). As member socket connections are running in the background, for determining if a particular connection has succeeded or failed it is recommended to use [`srt_connect_callback`](#srt_connect_callback). In this case the `token` callback function parameter will be the same as the `token` value used -for the particular item in the `name` connection table. +for the particular item in the `links` connection array. The `token` value doesn't have any limitations except that the -1 value is a "trap representation", that is, when set on input it will make the internals @@ -1534,7 +1547,7 @@ Deletes the configuration object. #### srt_config_add ``` -int srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); ``` Adds a configuration option to the configuration object. @@ -1564,12 +1577,6 @@ The following options are allowed to be set on the member socket: * [`SRTO_UDP_SNDBUF`](API-socket-options.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:---------------------------------- |:--------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | This option is not allowed to be set on a socket being a group member. Or if bonding API is disabled. | @@ -1595,15 +1602,11 @@ The following options are allowed to be set on the member socket: ### srt_getpeername ``` -int srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Retrieves the remote address to which the socket is connected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:------------------------------------------------------------------------ | @@ -1618,7 +1621,7 @@ Retrieves the remote address to which the socket is connected. ### srt_getsockname ``` -int srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Extracts the address to which the socket was bound. Although you should know @@ -1627,10 +1630,6 @@ useful for extracting the local outgoing port number when it was specified as 0 with binding for system autoselection. With this function you can extract the port number after it has been autoselected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:---------------------------------------------- | @@ -1659,8 +1658,8 @@ if (res < 0) { ### 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); +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); +SRTSTATUS 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). @@ -1678,11 +1677,6 @@ For most options, it will be the size of an integer. Some options, however, use The application is responsible for allocating sufficient memory space as defined and pointed to by `optval`. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:-------------------------------- |:---------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1697,8 +1691,8 @@ The application is responsible for allocating sufficient memory space as defined ### 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); +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); ``` Sets a value for a socket option in the socket or group. @@ -1715,11 +1709,6 @@ Please note that some of the options can only be set on sockets or only on groups, although most of the options can be set on the groups so that they are then derived by the member sockets. -| Returns | | -|:----------------------------- |:----------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:----------------------------------- |:--------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1728,7 +1717,7 @@ are then derived by the member sockets. | [`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 +**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). @@ -1743,7 +1732,7 @@ uint32_t srt_getversion(); ``` Get SRT version value. The version format in hex is 0xXXYYZZ for x.y.z in human -readable form, where x = ("%d", (version>>16) & 0xff), etc. +readable form. E.g. 0x012033 means version 1.20.33. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -1911,7 +1900,7 @@ single call to this function determines a message's boundaries. |:----------------------------- |:--------------------------------------------------------- | | Size | Size of the data sent, if successful | | `SRT_ERROR` | In case of error (-1) | -| | | +| | | **NOTE**: Note that in **file/stream mode** the returned size may be less than `len`, which means that it didn't send the whole contents of the buffer. You would need to @@ -1990,10 +1979,10 @@ the currently lost one, it will be delivered and the lost one dropped. | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | Size (\>0) of the data received, if successful. | +| Size value \> 0 | Size of the data received, if successful. | | 0 | If the connection has been closed | | `SRT_ERROR` | (-1) when an error occurs | -| | | +| | | | Errors | | |:--------------------------------------------- |:--------------------------------------------------------- | @@ -2046,7 +2035,7 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | The size (\>0) of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | +| Size value \> 0 | The size of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | | -1 | in case of error | | | | @@ -2090,10 +2079,10 @@ the required range already, so for a numbers like 0x7FFFFFF0 and 0x10, for which ### srt_bistats ``` // Performance monitor with Byte counters for better bitrate estimation. -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); ``` Reports the current statistics @@ -2109,12 +2098,6 @@ Reports the current statistics of the fields please refer to [SRT Statistics](statistics.md). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Invalid socket ID provided. @@ -2167,11 +2150,11 @@ function can then be used to block until any readiness status in the whole int srt_epoll_create(void); ``` -Creates a new epoll container. +Creates a new epoll container and returns its identifier (EID). | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| valid EID | Success | +| valid EID >= 0 | Success | | -1 | Failure | | | | @@ -2191,10 +2174,10 @@ Creates a new epoll container. ### srt_epoll_update_ssock ``` -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); ``` Adds a socket to a container, or updates an existing socket subscription. @@ -2274,12 +2257,6 @@ as level-triggered, you can do two separate subscriptions for the same socket. any possible flag, you must use [`srt_epoll_uwait`](#srt_epoll_uwait). Note that this function doesn't work with system file descriptors. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2298,8 +2275,8 @@ the [`SRT_ECONNSETUP`](#srt_econnsetup) code is predicted. ### srt_epoll_remove_ssock ``` -int srt_epoll_remove_usock(int eid, SRTSOCKET u); -int srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); ``` Removes a specified socket from an epoll container and clears all readiness @@ -2308,12 +2285,6 @@ states recorded for that socket. The `_usock` suffix refers to a user socket (SRT socket). The `_ssock` suffix refers to a system socket. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2372,7 +2343,7 @@ the only way to know what kind of error has occurred on the socket. | Returns | | |:----------------------------- |:------------------------------------------------------------ | -| Number | The number (\>0) of ready sockets, of whatever kind (if any) | +| Number \> 0 | The number of ready sockets, of whatever kind (if any) | | -1 | Error | | | | @@ -2411,7 +2382,7 @@ indefinitely until a readiness state occurs. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------- | -| Number | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | +| Number \> 0 | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | | `fdsSize` + 1 | This means that there was not enough space in the output array to report all events.
For events subscribed with the [`SRT_EPOLL_ET`](#SRT_EPOLL_ET) flag only those will be cleared that were reported.
Others will wait for the next call. | | 0 | If no readiness state was found on any socket and the timeout has passed
(this is not possible when waiting indefinitely) | | -1 | Error | @@ -2420,8 +2391,14 @@ indefinitely until a readiness state occurs. | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | -| [`SRT_EINVPARAM`](#srt_einvparam) | One of possible usage errors:
* `fdsSize` is < 0
* `fdsSize` is > 0 and `fdsSet` is a null pointer
* [`eid`](#eid) was subscribed to any system socket | -| | | +| [`SRT_EINVPARAM`](#srt_einvparam) | Usage error (see below) | +| | | + +Usage errors reported as `SRT_EINVPARAM`: + +* `fdsSize` is \< 0 +* `fdsSize` is \> 0 and `fdsSet` is a null pointer +* [`eid`](#eid) was subscribed to any system socket **IMPORTANT**: This function reports timeout by returning 0, not by [`SRT_ETIMEOUT`](#srt_etimeout) error. @@ -2451,18 +2428,12 @@ closed and its state can be verified with a call to [`srt_getsockstate`](#srt_ge ### srt_epoll_clear_usocks ``` -int srt_epoll_clear_usocks(int eid); +SRTSTATUS srt_epoll_clear_usocks(int eid); ``` This function removes all SRT ("user") socket subscriptions from the epoll container identified by [`eid`](#eid). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2504,7 +2475,7 @@ 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 | +| `SRT_ERROR` (-1) | Special value in case when an error occurred | | | | | Errors | | @@ -2519,17 +2490,11 @@ the general output array is not empty. ### srt_epoll_release ``` -int srt_epoll_release(int eid); +SRTSTATUS srt_epoll_release(int eid); ``` Deletes the epoll container. -| Returns | | -|:----------------------------- |:-------------------------------------------------------------- | -| | The number (\>0) of ready sockets, of whatever kind (if any) | -| -1 | Error | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2745,7 +2710,7 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistic | Returns | | |:----------------------------- |:--------------------------------------------------------------------------- | | | Connection time in microseconds elapsed since epoch of SRT internal clock | -| -1 | Error | +| `SRT_ERROR` (-1) | Error | | | | | Errors | | @@ -2897,7 +2862,7 @@ The actual messages assigned to the internal rejection codes, that is, less than ### srt_setrejectreason ``` -int srt_setrejectreason(SRTSOCKET sock, int value); +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); ``` Sets the rejection code on the socket. This call is only useful in the listener @@ -2911,12 +2876,6 @@ can inform the calling side that the resource specified under the `r` key in the 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 | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Error | -| -1 | Success | -| | | - | Errors | | |:--------------------------------- |:-------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket `sock` is not an ID of a valid socket | @@ -2942,6 +2901,8 @@ used for the connection, the function should also be called when the a numeric code, which can be translated into a message by [`srt_rejectreason_str`](#srt_rejectreason_str). +The returned value is one of the values listed in enum `SRT_REJECT_REASON`. +For an invalid value of `sock` the `SRT_REJ_UNKNOWN` is returned. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -2952,7 +2913,8 @@ a numeric code, which can be translated into a message by #### SRT_REJ_UNKNOWN -A fallback value for cases when there was no connection rejected. +A fallback value for cases when there was no connection rejected or the +reason cannot be obtained. #### SRT_REJ_SYSTEM @@ -3139,7 +3101,8 @@ is no longer usable. #### SRT_ECONNFAIL -General connection failure of unknown details. +General connection failure of unknown details (currently is not reported +directly by any API function and it's reserved for future use). #### SRT_ECONNLOST @@ -3357,34 +3320,37 @@ Example 1: * Socket 1: bind to IPv4 0.0.0.0 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = true -* Result: NOT intersecting +* Result: NOT intersecting, allowed to proceed Example 2: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv4 0.0.0.0 -* Result: intersecting (and conflicting) +* Result: failure: 0.0.0.0 encloses 1.2.3.4, so they are in conflict Example 3: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = false -* Result: intersecting (and conflicting) +* Result: failure: this encloses all IPv4, so it conflicts with 1.2.3.4 -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: +Binding another socket to an endpoint that is already bound by another +socket is possible, and results in a shared binding, as long as the binding +address that is enclosed by this existing binding is exactly identical to +the specified one and 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. +3. The UDP-specific settings (SRT options that map to UDP options) must be identical. If any of these conditions isn't satisfied, the `srt_bind` function results in conflict and report this error. + #### SRT_EASYNCFAIL General asynchronous failure (not in use currently). diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 35252ada0..6b32efe16 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -236,12 +236,12 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -SRTSTATUS srt::CUDTUnited::startup() +SRTRUNSTATUS srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return SRTSTATUS(1); + return SRT_RUN_ALREADY; // Global initialization code #ifdef _WIN32 @@ -258,18 +258,18 @@ SRTSTATUS srt::CUDTUnited::startup() PacketFilter::globalInit(); if (m_bGCStatus) - return SRTSTATUS(1); + return SRT_RUN_ALREADY; m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) - return SRT_ERROR; + return SRT_RUN_ERROR; m_bGCStatus = true; HLOGC(inlog.Debug, log << "SRT Clock Type: " << SRT_SYNC_CLOCK_STR); - return SRT_STATUS_OK; + return SRT_RUN_OK; } SRTSTATUS srt::CUDTUnited::cleanup() @@ -3363,7 +3363,7 @@ void* srt::CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -SRTSTATUS srt::CUDT::startup() +SRTRUNSTATUS srt::CUDT::startup() { return uglobal().startup(); } @@ -4355,7 +4355,7 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) namespace UDT { -SRTSTATUS startup() +SRTRUNSTATUS startup() { return srt::CUDT::startup(); } diff --git a/srtcore/api.h b/srtcore/api.h index 287336b32..616b7840b 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -245,7 +245,7 @@ class CUDTUnited /// initialize the UDT library. /// @return 0 if success, otherwise -1 is returned. - SRTSTATUS startup(); + SRTRUNSTATUS startup(); /// release the UDT library. /// @return 0 if success, otherwise -1 is returned. diff --git a/srtcore/core.h b/srtcore/core.h index d72bb072e..d836a7775 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -190,7 +190,7 @@ class CUDT ~CUDT(); public: //API - static SRTSTATUS startup(); + static SRTRUNSTATUS startup(); static SRTSTATUS cleanup(); static SRTSOCKET socket(); #if ENABLE_BONDING diff --git a/srtcore/srt.h b/srtcore/srt.h index 682a6fe26..9b2e2c423 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -145,6 +145,7 @@ written by // This is normal and should be normally used. typedef int32_t SRTSOCKET; typedef int SRTSTATUS; +typedef int SRTRUNSTATUS; #else // Used for development only. #include "../common/devel_util.h" @@ -770,21 +771,29 @@ static const SRTSOCKET SRT_INVALID_SOCK (-1); static const SRTSOCKET SRT_SOCKID_CONNREQ (0); static const SRTSTATUS SRT_ERROR (-1); static const SRTSTATUS SRT_STATUS_OK (0); +static const SRTRUNSTATUS SRT_RUN_ERROR (-1); +static const SRTRUNSTATUS SRT_RUN_OK (0); +static const SRTRUNSTATUS SRT_RUN_ALREADY (1); -#else + +#else // C version static const SRTSOCKET SRT_INVALID_SOCK = -1; static const SRTSOCKET SRT_SOCKID_CONNREQ = 0; static const SRTSTATUS SRT_ERROR = -1; static const SRTSTATUS SRT_STATUS_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ERROR = -1; +static const SRTRUNSTATUS SRT_RUN_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ALREADY = 1; #endif + typedef struct CBytePerfMon SRT_TRACEBSTATS; // library initialization -SRT_API SRTSTATUS srt_startup(void); +SRT_API SRTRUNSTATUS srt_startup(void); SRT_API SRTSTATUS srt_cleanup(void); // diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 011f277ae..de2605db7 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -29,7 +29,7 @@ using namespace srt; extern "C" { -SRTSTATUS srt_startup() { return CUDT::startup(); } +SRTRUNSTATUS srt_startup() { return CUDT::startup(); } SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } // Socket creation. diff --git a/srtcore/udt.h b/srtcore/udt.h index c0e1a53ff..5cc63aa41 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -168,7 +168,7 @@ SRT_API extern const SRTSOCKET INVALID_SOCK; #undef ERROR SRT_API extern const SRTSTATUS ERROR; -SRT_API SRTSTATUS startup(); +SRT_API SRTRUNSTATUS startup(); SRT_API SRTSTATUS cleanup(); SRT_API SRTSOCKET socket(); inline SRTSOCKET socket(int , int , int ) { return socket(); } From be302ca330b4dda06acd3fa3dcbec841c3f8d0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 20 Feb 2024 10:26:21 +0100 Subject: [PATCH 090/174] Removed srt::isgroup. Reused CUDT::isgroup where applicable. Added missing license heading. --- common/devel_util.h | 28 ++++++++++++++++++++++++++++ srtcore/api.cpp | 28 ++++++++++++++-------------- srtcore/srt.h | 6 ------ srtcore/srt_c_api.cpp | 4 +++- testing/testmedia.cpp | 2 +- testing/testmedia.hpp | 2 +- 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/common/devel_util.h b/common/devel_util.h index b908cedc6..4e31af07e 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -1,3 +1,31 @@ +/* + * 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/. + * + */ + +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// IMPORTANT!!! +// +// This is normally not a part of SRT source files. This is a developer utility +// that allows developers to perform a compile test on a version instrumented +// with type checks. To do that, you can do one of two things: +// +// - configure the compiling process with extra -DSRT_TEST_FORCED_CONSTANT=1 flag +// - unblock the commented out #define in srt.h file for that constant +// +// Note that there's no use of such a compiled code. This is done only so that +// the compiler can detect any misuses of the SRT symbolic type names and +// constants. + #include diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 6b32efe16..660a07251 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -874,7 +874,7 @@ SRTSTATUS srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_cal try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->installConnectHook(hook, opaq); @@ -1214,7 +1214,7 @@ SRTSOCKET srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const s // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); // Note: forced_isn is ignored when connecting a group. @@ -1255,7 +1255,7 @@ SRTSOCKET srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); @@ -1894,7 +1894,7 @@ void srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->close(); @@ -2352,7 +2352,7 @@ void srt::CUDTUnited::epoll_clear_usocks(int eid) void srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); m_EPoll.update_usock(eid, u, events); @@ -2430,7 +2430,7 @@ void srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) #if ENABLE_BONDING CUDTGroup* g = 0; - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); g = k.group; @@ -3504,7 +3504,7 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) SRTSTATUS srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { - if (!isgroup(groupid) || !psize) + if (!CUDT::isgroup(groupid) || !psize) { return APIError(MJ_NOTSUP, MN_INVAL, 0); } @@ -3675,7 +3675,7 @@ SRTSOCKET srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], if (arraysize <= 0) return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; - if (!isgroup(grp)) + if (!CUDT::isgroup(grp)) { // connectLinks accepts only GROUP id, not socket id. return APIError(MJ_NOTSUP, MN_SIDINVAL, 0), SRT_INVALID_SOCK; @@ -3786,7 +3786,7 @@ SRTSTATUS srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_ try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); @@ -3817,7 +3817,7 @@ SRTSTATUS srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const voi try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); @@ -3862,7 +3862,7 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); @@ -3906,7 +3906,7 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); @@ -4251,7 +4251,7 @@ srt::CUDTException& srt::CUDT::getlasterror() SRTSTATUS srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) return groupsockbstats(u, perf, clear); #endif @@ -4329,7 +4329,7 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->getStatus(); diff --git a/srtcore/srt.h b/srtcore/srt.h index 9b2e2c423..b18530257 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -164,12 +164,6 @@ extern "C" { // socket or a socket group. static const int32_t SRTGROUP_MASK = (1 << 30); -#ifdef __cplusplus -namespace srt { -inline bool isgroup(SRTSOCKET sid) { return (int32_t(sid) & SRTGROUP_MASK) != 0; } -} -#endif - #ifdef _WIN32 typedef SOCKET SYSSOCKET; #else diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index de2605db7..030d038b9 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -126,8 +126,10 @@ SRTSOCKET srt_connect_bind(SRTSOCKET u, SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { - if (isgroup(u)) +#if ENABLE_BONDING + if (CUDT::isgroup(u)) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); +#endif bool yes = 1; CUDT::setsockopt(u, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index a5c47928e..0b99835bf 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -570,7 +570,7 @@ void SrtCommon::AcceptNewClient() } #if ENABLE_BONDING - if (srt::isgroup(m_sock)) + if (int32_t(m_sock) & SRTGROUP_MASK) { m_listener_group = true; if (m_group_config != "") diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index f7c49ddf6..5441c4d56 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -142,7 +142,7 @@ class SrtCommon void Acquire(SRTSOCKET s) { m_sock = s; - if (srt::isgroup(s)) + if (int32_t(s) & SRTGROUP_MASK) m_listener_group = true; } From 283021efbaca001e3b0f0de8fdea51b8de173ff9 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 28 Feb 2024 11:03:46 +0100 Subject: [PATCH 091/174] Apply suggestions from code review (further impossible fixes pending) Co-authored-by: Maxim Sharabayko --- srtcore/buffer_rcv.cpp | 1 - srtcore/buffer_rcv.h | 8 ++++---- srtcore/core.cpp | 4 ++-- srtcore/core.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 97b76d755..6d6ebd841 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -656,7 +656,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairstatus_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) // // thread safety: -// start_pos_: CUDT::m_RecvLock +// m_iStartPos: CUDT::m_RecvLock // first_unack_pos_: CUDT::m_AckLock -// max_pos_inc_: none? (modified on add and ack -// first_nonread_pos_: +// m_iMaxPosOff: none? (modified on add and ack +// m_iFirstNonreadPos: // // // m_iStartPos: the first packet that should be read (might be empty) @@ -455,7 +455,7 @@ class CRcvBuffer return m_iMaxPosOff; } - // Unfortunately it still needs locking. + // Returns true if the buffer is full. Requires locking. bool full() const { return size() == capacity(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eaea48102..9afa74c9b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5544,7 +5544,7 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } - SRT_ATR_UNUSED bool wakeup_on_signal = true; + SRT_ATR_UNUSED bool bWakeupOnSignal = true; // We may just briefly unlocked the m_RecvLock, so we need to check m_bClosing again to avoid deadlock. if (self->m_bClosing) @@ -8183,7 +8183,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #endif - // Signalling m_RecvDataCond is not dane when TSBPD is on. + // Signalling m_RecvDataCond is not done when TSBPD is on. // This signalling is done in file mode in order to keep the // API reader thread sleeping until there is a "bigger portion" // of data to read. In TSBPD mode this isn't done because every diff --git a/srtcore/core.h b/srtcore/core.h index 8cff2fbce..dee7bd48d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -987,7 +987,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet + sync::atomic m_bWakeOnRecv; // Expected to wake up TSBPD when a read-ready data packet is received. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; From b969a8329e9275d6df2f8979a4b803de7499eaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 28 Feb 2024 12:06:29 +0100 Subject: [PATCH 092/174] Remaining post-review fixes --- srtcore/buffer_rcv.h | 5 +-- srtcore/core.cpp | 83 ++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 93b51c2c8..24f5066f6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -338,9 +338,10 @@ class CRcvBuffer /// Read the whole message from one or several packets. /// - /// @param [in,out] data buffer to write the message into. + /// @param [out] data buffer to write the message into. /// @param [in] len size of the buffer. - /// @param [in,out] message control data + /// @param [out,opt] message control data to be filled + /// @param [out,opt] pw_seqrange range of sequence numbers for packets belonging to the message /// /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9afa74c9b..45aeb2b4c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5562,7 +5562,7 @@ void * srt::CUDT::tsbpd(void* param) log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); + bWakeupOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5585,7 +5585,7 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (bWakeupOnSignal? "signal" : "timeout") << "]!!! - " << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); @@ -8048,14 +8048,13 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); return false; } - { - ScopedLock buflock (m_RcvBufferLock); - bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); - if (has_followers) - w_log_reason = "first lost"; - else - w_log_reason = "expected next"; - } + + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; return true; } @@ -9090,34 +9089,27 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } } - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - // XXX Likely this is not necessary because: - // 1. In the recv-waiting state, that is, when TSBPD thread - // sleeps forever, it will be woken up anyway on packet - // reception. - // 2. If there are any packets in the buffer and the initial - // packet cell is empty (in which situation any drop could - // occur), TSBPD thread is sleeping timely, until the playtime - // of the first "drop up to" packet. Dropping changes nothing here. - // 3. If the buffer is empty, there's nothing "to drop up to", so - // this function will not change anything in the buffer and so - // in the reception state as well. - // 4. If the TSBPD thread is waiting until a play-ready packet is - // retrieved by the API call (in which case it is also sleeping - // forever until it's woken up by the API call), it may remain - // stalled forever, if the application isn't reading the play-ready - // packet. But this means that the application got stalled anyway. - // TSBPD when woken up could at best state that there's still a - // play-ready packet that is still not retrieved and fall back - // to sleep (forever). - - //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - //rcvtscc.notify_one(); - } + + // NOTE: + // PREVIOUSLY done: notify on rcvtscc if m_bTsbPd + // OLD COMMENT: + // // When the drop request was received, it means that there are + // // packets for which there will never be ACK sent; if the TSBPD thread + // // is currently in the ACK-waiting state, it may never exit. + // + // Likely this is no longer necessary because: + // + // 1. If there's a play-ready packet, either in cell 0 or + // after a drop, TSBPD is sleeping timely, up to the play-time + // of the next ready packet (and the drop region concerned here + // is still a gap to be skipped for this). + // 2. TSBPD sleeps forever when the buffer is empty, in which case + // it will be always woken up on packet reception (see m_bWakeOnRecv). + // And dropping won't happen in that case anyway. Note that the drop + // request will not drop packets that are already received. + // 3. TSBPD sleeps forever when the API call didn't extract the + // data that are ready to play. This isn't a problem if nothing + // except the API call would wake it up. } dropFromLossLists(dropdata[0], dropdata[1]); @@ -10679,21 +10671,6 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } - - // This should not be required with the new receiver buffer because - // signalling happens on every packet reception, if it has changed the - // earliest packet position. - /* - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } - else - { - HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); - } - */ } // Separately report loss records of those reported by a filter. From 6ebf94f0e4b19874353c0d412ff744f849b86a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 5 Mar 2024 09:39:38 +0100 Subject: [PATCH 093/174] Withdrawn changes from #2858 as they break ABI compat --- srtcore/api.cpp | 10 ++++---- srtcore/logging.cpp | 4 +++- srtcore/logging.h | 57 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index f9ac6a8d1..e77469de2 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4678,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4687,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d309b1b8a..c1625916d 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,6 +23,8 @@ using namespace std; namespace srt_logging { +/* Blocked temporarily until 1.6.0. This causes ABI incompat with 1.5.0 line. + // Note: subscribe() and unsubscribe() functions are being called // in the global constructor and destructor only, as the // Logger objects (and inside them also their LogDispatcher) @@ -85,7 +87,7 @@ void LogDispatcher::SendLogLine(const char* file, int line, const std::string& a } src_config->unlock(); } - +*/ #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index c17781c24..88a2bbfa3 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,7 @@ written by #include #include #include -#include +//#include #include #include #ifdef _WIN32 @@ -116,7 +116,7 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - std::vector loggers; + // std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,10 +139,11 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } - +/* void subscribe(LogDispatcher*); void unsubscribe(LogDispatcher*); void updateLoggersState(); + */ }; // The LogDispatcher class represents the object that is responsible for @@ -154,7 +155,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - srt::sync::atomic enabled; +// srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -165,7 +166,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), - enabled(false), +// enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -193,18 +194,17 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } - config.subscribe(this); - Update(); +// config.subscribe(this); +// Update(); } ~LogDispatcher() { - src_config->unsubscribe(this); +// src_config->unsubscribe(this); } - void Update(); - - bool CheckEnabled() { return enabled; } + // void Update(); + bool CheckEnabled(); // { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,6 +428,22 @@ class Logger } }; +inline bool LogDispatcher::CheckEnabled() +{ + // Don't use enabler caching. Check enabled state every time. + + // These assume to be atomically read, so the lock is not needed + // (note that writing to this field is still mutex-protected). + // It's also no problem if the level was changed at the moment + // when the enabler check is tested here. Worst case, the log + // will be printed just a moment after it was turned off. + const LogConfig* config = src_config; // to enforce using const operator[] + int configured_enabled_fa = config->enabled_fa[fa]; + int configured_maxlevel = config->max_level; + + return configured_enabled_fa && level <= configured_maxlevel; +} + #if HAVE_CXX11 @@ -478,7 +494,24 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); +} + } #endif // INC_SRT_LOGGING_H - From 3601861c13ff8da2d1b7e4d7ec9a5e04978a9f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 5 Mar 2024 10:12:33 +0100 Subject: [PATCH 094/174] [core] Restored logger perf improvements that were blocked up to 1.6.0 --- srtcore/api.cpp | 10 ++++---- srtcore/logging.cpp | 4 +--- srtcore/logging.h | 56 +++++++++------------------------------------ 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e77469de2..f9ac6a8d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4678,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4687,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index c1625916d..d309b1b8a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,8 +23,6 @@ using namespace std; namespace srt_logging { -/* Blocked temporarily until 1.6.0. This causes ABI incompat with 1.5.0 line. - // Note: subscribe() and unsubscribe() functions are being called // in the global constructor and destructor only, as the // Logger objects (and inside them also their LogDispatcher) @@ -87,7 +85,7 @@ void LogDispatcher::SendLogLine(const char* file, int line, const std::string& a } src_config->unlock(); } -*/ + #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index 88a2bbfa3..2d599bb49 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,7 @@ written by #include #include #include -//#include +#include #include #include #ifdef _WIN32 @@ -116,7 +116,7 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - // std::vector loggers; + std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,11 +139,10 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } -/* + void subscribe(LogDispatcher*); void unsubscribe(LogDispatcher*); void updateLoggersState(); - */ }; // The LogDispatcher class represents the object that is responsible for @@ -155,7 +154,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; -// srt::sync::atomic enabled; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -166,7 +165,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), -// enabled(false), + enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -194,17 +193,18 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } -// config.subscribe(this); -// Update(); + config.subscribe(this); + Update(); } ~LogDispatcher() { -// src_config->unsubscribe(this); + src_config->unsubscribe(this); } - // void Update(); - bool CheckEnabled(); // { return enabled; } + void Update(); + + bool CheckEnabled() { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,22 +428,6 @@ class Logger } }; -inline bool LogDispatcher::CheckEnabled() -{ - // Don't use enabler caching. Check enabled state every time. - - // These assume to be atomically read, so the lock is not needed - // (note that writing to this field is still mutex-protected). - // It's also no problem if the level was changed at the moment - // when the enabler check is tested here. Worst case, the log - // will be printed just a moment after it was turned off. - const LogConfig* config = src_config; // to enforce using const operator[] - int configured_enabled_fa = config->enabled_fa[fa]; - int configured_maxlevel = config->max_level; - - return configured_enabled_fa && level <= configured_maxlevel; -} - #if HAVE_CXX11 @@ -494,24 +478,6 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - (*src_config->log_stream).flush(); - } - src_config->unlock(); -} - } #endif // INC_SRT_LOGGING_H From d9f079a3648acd219608784135201bf5e4d4494b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 3 Jun 2024 10:32:53 +0200 Subject: [PATCH 095/174] Turned integer-based values to strong types. CHECKPOINT: tests passed --- srtcore/buffer_rcv.cpp | 394 ++++++++++++++++++++++++----------------- srtcore/buffer_rcv.h | 215 ++++++++++++++++++---- srtcore/common.h | 2 + srtcore/core.cpp | 32 +--- srtcore/utilities.h | 20 ++- 5 files changed, 435 insertions(+), 228 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 6d6ebd841..ab72670df 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -78,18 +78,19 @@ namespace { // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) + bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosOff) % iSize; - const bool isOverrun = iLastPos < iStartPos; + //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; + const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; if (isOverrun) - return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; } } @@ -120,14 +121,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(0) - , m_iEndPos(0) - , m_iDropPos(0) - , m_iFirstNonreadPos(0) + , m_iStartPos(&m_szSize, 0) + , m_iEndPos(&m_szSize, 0) + , m_iDropPos(&m_szSize, 0) + , m_iFirstNonreadPos(&m_szSize, 0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(-1) + , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -152,29 +153,34 @@ CRcvBuffer::~CRcvBuffer() void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { - HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos - << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source + << ") start=" << m_iStartPos VALUE + << " end=" << m_iEndPos VALUE + << " drop=" << m_iDropPos VALUE + << " max-off=+" << m_iMaxPosOff VALUE + << " seq[start]=%" << m_iStartSeqNo VALUE); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno); IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - if (offset < 0) + if (offset < COff(0)) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); return InsertInfo(InsertInfo::BELATED); } IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); - if (offset >= (int)capacity()) + if (offset >= COff(capacity())) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); @@ -188,14 +194,14 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); - const int newpktpos = incPos(m_iStartPos, offset); - const int prev_max_off = m_iMaxPosOff; + const CPos newpktpos = m_iStartPos + offset; + const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) { - m_iMaxPosOff = offset + 1; + m_iMaxPosOff = offset + COff(1); extended_end = true; } @@ -204,7 +210,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -247,7 +253,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - int fallback_pos = -1; + CPos fallback_pos = CPos_TRAP; if (!m_tsbpd.isEnabled()) { // In case when TSBPD is off, we take into account the message mode @@ -271,22 +277,23 @@ void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); if (pkt) { - w_if.first_seq = pkt->getSeqNo(); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } } -const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) { if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = offPos(m_iStartPos, m_iEndPos); + w_span = m_iEndPos - m_iStartPos; } - if (pos == -1) + if (pos == CPos_TRAP) { - w_span = 0; + w_span = COff(0); return NULL; } @@ -297,15 +304,16 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) // be implemented for message mode, but still this would employ // a separate begin-end range declared for a complete out-of-order // message. - w_span = 1; + w_span = COff(1); return &packetAt(pos); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) { time_point earlier_time; - int prev_max_pos = incPos(m_iStartPos, prev_max_off); + //int prev_max_pos = incPos(m_iStartPos, prev_max_off); + CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -320,7 +328,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -328,7 +337,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -361,7 +371,9 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // CONSIDER: make m_iDropPos rather m_iDropOff, this will make // this comparison a simple subtraction. Note that offset will // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) + + //else if (cmpPos(newpktpos, m_iDropPos) < 0) + else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -396,12 +408,12 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr return earlier_time; } -void CRcvBuffer::updateGapInfo(int prev_max_pos) +void CRcvBuffer::updateGapInfo(CPos prev_max_pos) { - int pos = m_iEndPos; + CPos pos = m_iEndPos; // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status == EntryState_Empty) { @@ -420,7 +432,7 @@ void CRcvBuffer::updateGapInfo(int prev_max_pos) m_iEndPos = pos; m_iDropPos = pos; // fallback, although SHOULD be impossible // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status != EntryState_Empty) { @@ -440,7 +452,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); - int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); + //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -451,25 +464,27 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; - while (len > 0) + const int iDropCnt = len VALUE; + while (len VALUE > 0) { dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - m_iStartPos = incPos(m_iStartPos); + //m_iStartPos = incPos(m_iStartPos); + ++m_iStartPos; --len; } // Update positions - m_iStartSeqNo = seqno; + m_iStartSeqNo = CSeqNo(seqno); // Move forward if there are "read/drop" entries. // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -490,7 +505,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -502,24 +518,28 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); + //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; + const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); return 0; } const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING); - int minDroppedOffset = -1; + COff minDroppedOffset = COff(-1); int iDropCnt = 0; - const int start_off = max(0, offset_a); - const int start_pos = incPos(m_iStartPos, start_off); - const int end_off = min((int) m_szSize - 1, offset_b + 1); - const int end_pos = incPos(m_iStartPos, end_off); + const COff start_off = COff(max(0, offset_a)); + //const int start_pos = incPos(m_iStartPos, start_off); + const CPos start_pos = m_iStartPos + start_off; + const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); + //const int end_pos = incPos(m_iStartPos, end_off); + const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (int i = start_pos; i != end_pos; i = incPos(i)) + for (CPos i = start_pos; i != end_pos; ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -557,7 +577,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -567,8 +588,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - const int stop_pos = decPos(m_iStartPos); - for (int i = start_pos; i != stop_pos; i = decPos(i)) + //const int stop_pos = decPos(m_iStartPos); + const CPos stop_pos = m_iStartPos - COff(1); + for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -591,7 +613,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -619,7 +642,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (!m_tsbpd.isEnabled() && m_bMessageAPI) { if (!checkFirstReadableNonOrder()) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; updateFirstReadableNonOrder(); } @@ -632,16 +655,20 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iStartPos == m_iEndPos) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); - w_seq = m_iStartSeqNo; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); + w_seq = m_iStartSeqNo VALUE; return m_iMaxPosOff > 0; } - int end_off = offPos(m_iStartPos, m_iEndPos); + //int end_off = offPos(m_iStartPos, m_iEndPos); + COff end_off = m_iEndPos - m_iStartPos; - w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + << " maxD=" << m_iMaxPosOff VALUE + << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); return (end_off < m_iMaxPosOff); @@ -650,13 +677,13 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos == CPos_TRAP) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; + const CPos readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -670,7 +697,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); - m_iStartSeqNo = CSeqNo::incseq(pktseqno); + m_iStartSeqNo = CSeqNo(pktseqno) + 1; } else { @@ -747,7 +774,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - p = incPos(p); + //p = incPos(p); + ++p; m_iNotch = 0; m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff >= 0); - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; } else m_iNotch += rs; @@ -923,7 +961,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -942,12 +981,13 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos); + //return offPos(m_iStartPos, m_iFirstNonreadPos); + return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -958,7 +998,8 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -966,16 +1007,18 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - lastpos = decPos(lastpos); + //lastpos = decPos(lastpos); + --lastpos; } - + if (m_entries[lastpos].pUnit == NULL) return 0; - int startpos = m_iStartPos; + CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - startpos = incPos(startpos); + //startpos = incPos(startpos); + ++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1034,8 +1077,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - return std::pair(m_iStartSeqNo, seqno_last); + //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); + const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + return std::pair(m_iStartSeqNo VALUE, seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1047,7 +1092,7 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const return true; SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } if (!haveInorderPackets) @@ -1072,7 +1117,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no return info; } SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) { SRT_ASSERT(m_numNonOrderPackets > 0); const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); @@ -1107,7 +1152,7 @@ void CRcvBuffer::countBytes(int pkts, int bytes) } } -void CRcvBuffer::releaseUnitInPos(int pos) +void CRcvBuffer::releaseUnitInPos(CPos pos) { CUnit* tmp = m_entries[pos].pUnit; m_entries[pos] = Entry(); // pUnit = NULL; status = Empty @@ -1115,7 +1160,7 @@ void CRcvBuffer::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } -bool CRcvBuffer::dropUnitInPos(int pos) +bool CRcvBuffer::dropUnitInPos(CPos pos) { if (!m_entries[pos].pUnit) return false; @@ -1127,7 +1172,7 @@ bool CRcvBuffer::dropUnitInPos(int pos) { --m_numNonOrderPackets; if (pos == m_iFirstNonOrderMsgPos) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; } releaseUnitInPos(pos); return true; @@ -1135,12 +1180,14 @@ bool CRcvBuffer::dropUnitInPos(int pos) void CRcvBuffer::releaseNextFillerEntries() { - int pos = m_iStartPos; + CPos pos = m_iStartPos; while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; releaseUnitInPos(pos); - pos = incPos(pos); + //pos = incPos(pos); + ++pos; m_iStartPos = pos; --m_iMaxPosOff; if (m_iMaxPosOff < 0) @@ -1154,15 +1201,16 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. - int pos = m_iFirstNonreadPos; + CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (int i = pos; i != end_pos; i = incPos(i)) + for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1176,7 +1224,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = incPos(i); + m_iFirstNonreadPos = i + COff(1); break; } } @@ -1188,9 +1237,9 @@ void CRcvBuffer::updateNonreadPos() } } -int CRcvBuffer::findLastMessagePkt() +CPos CRcvBuffer::findLastMessagePkt() { - for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1200,10 +1249,10 @@ int CRcvBuffer::findLastMessagePkt() } } - return -1; + return CPos_TRAP; } -void CRcvBuffer::onInsertNonOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(CPos insertPos) { if (m_numNonOrderPackets == 0) return; @@ -1214,7 +1263,7 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) return; // Just a sanity check. This function is called when a new packet is added. @@ -1233,14 +1282,14 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (scanNonOrderMessageRight(insertPos, msgNo) != CPos_TRAP); if (!hasLast) return; - const int firstPktPos = (boundary & PB_FIRST) + const CPos firstPktPos = (boundary & PB_FIRST) ? insertPos : scanNonOrderMessageLeft(insertPos, msgNo); - if (firstPktPos < 0) + if (firstPktPos == CPos_TRAP) return; m_iFirstNonOrderMsgPos = firstPktPos; @@ -1249,12 +1298,13 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1277,7 +1327,7 @@ bool CRcvBuffer::checkFirstReadableNonOrder() void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos != CPos_TRAP) return; if (m_iMaxPosOff == 0) @@ -1288,17 +1338,19 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); - int posFirst = -1; - int posLast = -1; + CPos posFirst = CPos_TRAP; + CPos posLast = CPos_TRAP; int msgNo = -1; - for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) { if (!m_entries[pos].pUnit) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1306,7 +1358,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgOrderFlag()) // Skip in order packet { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1321,7 +1374,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1338,18 +1392,20 @@ void CRcvBuffer::updateFirstReadableNonOrder() return; } -int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (startPos == lastPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = incPos(pos); + //pos = incPos(pos); + ++pos; if (!m_entries[pos].pUnit) break; @@ -1358,7 +1414,7 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1366,30 +1422,31 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const return pos; } while (pos != lastPos); - return -1; + return CPos_TRAP; } -int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. if (startPos == m_iStartPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = decPos(pos); + //pos = decPos(pos); + --pos; if (!m_entries[pos].pUnit) - return -1; + return CPos_TRAP; const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1397,7 +1454,7 @@ int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const return pos; } while (pos != m_iStartPos); - return -1; + return CPos_TRAP; } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) @@ -1435,12 +1492,12 @@ void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp) m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); } -string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1451,7 +1508,7 @@ string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNo if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1502,35 +1559,39 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { - int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; + COff offset (offset_val); // Check if it's still inside the buffer - if (offset < 0 || offset >= m_iMaxPosOff) + if (offset_val < 0 || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq - << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq + << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } // Start position - int frompos = incPos(m_iStartPos, offset); + //int frompos = incPos(m_iStartPos, offset); + CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then // m_iEnd stands on the first loss, unless it's equal // to the position pointed by m_iMaxPosOff. - int32_t ret_seq = SRT_SEQNO_NONE; - int ret_off = m_iMaxPosOff; + CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); + COff ret_off = m_iMaxPosOff; - int end_off = offPos(m_iStartPos, m_iEndPos); - if (frompos < end_off) + COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } } @@ -1544,11 +1605,13 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - int pos = incPos(m_iStartPos, offset); + //int pos = incPos(m_iStartPos, offset); + const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; - ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + ret_seq = m_iStartSeqNo + offset VALUE; break; } } @@ -1559,26 +1622,31 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Also no need to search anything if only the beginning was // being looked for. - if (ret_seq == SRT_SEQNO_NONE || !pw_end) - return ret_seq; + if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) + return ret_seq VALUE; // We want also the end range, so continue from where you // stopped. // Start from ret_off + 1 because we know already that ret_off // points to an empty cell. - for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - int pos = incPos(m_iStartPos, off); + //int pos = incPos(m_iStartPos, off); + const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); - return ret_seq; + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; + return ret_seq VALUE; } } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" + //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) + << (m_iStartSeqNo + ret_off) VALUE + << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 24f5066f6..18450947d 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -15,10 +15,162 @@ #include "common.h" #include "queue.h" #include "tsbpd_time.h" +#include "utilities.h" + +#define USE_WRAPPERS 1 +#define USE_OPERATORS 1 namespace srt { +// DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) + +#if USE_WRAPPERS +struct CPos +{ + int value; + const size_t* psize; + + int isize() const {return *psize;} + + explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + int val() const { return value; } + explicit operator int() const {return value;} + + CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos& operator=(const CPos& src) + { + psize = src.psize; + value = src.value; + return *this; + } + + int cmp(CPos other, CPos start) const + { + int pos2 = value; + int pos1 = other.value; + + const int off1 = pos1 >= start.value ? pos1 - start.value : pos1 + start.isize() - start.value; + const int off2 = pos2 >= start.value ? pos2 - start.value : pos2 + start.isize() - start.value; + + return off2 - off1; + } + + CPos& operator--() + { + if (value == 0) + value = isize() - 1; + else + --value; + return *this; + } + + CPos& operator++() + { + ++value; + if (value == isize()) + value = 0; + return *this; + } + + bool operator == (CPos other) const { return value == other.value; } + bool operator != (CPos other) const { return value != other.value; } +}; + +struct COff +{ + int value; + explicit COff(int v): value(v) {} + COff& operator=(int v) { value = v; return *this; } + + int val() const { return value; } + explicit operator int() const {return value;} + + COff& operator--() { --value; return *this; } + COff& operator++() { ++value; return *this; } + + COff operator--(int) { int v = value; --value; return COff(v); } + COff operator++(int) { int v = value; ++value; return COff(v); } + + COff operator+(COff other) const { return COff(value + other.value); } + COff operator-(COff other) const { return COff(value - other.value); } + COff& operator+=(COff other) { value += other.value; return *this;} + COff& operator-=(COff other) { value -= other.value; return *this;} + + bool operator == (COff other) const { return value == other.value; } + bool operator != (COff other) const { return value != other.value; } + bool operator < (COff other) const { return value < other.value; } + bool operator > (COff other) const { return value > other.value; } + bool operator <= (COff other) const { return value <= other.value; } + bool operator >= (COff other) const { return value >= other.value; } + + // Exceptionally allow modifications of COff by a bare integer + COff operator+(int other) const { return COff(value + other); } + COff operator-(int other) const { return COff(value - other); } + COff& operator+=(int other) { value += other; return *this;} + COff& operator-=(int other) { value -= other; return *this;} + + bool operator == (int other) const { return value == other; } + bool operator != (int other) const { return value != other; } + bool operator < (int other) const { return value < other; } + bool operator > (int other) const { return value > other; } + bool operator <= (int other) const { return value <= other; } + bool operator >= (int other) const { return value >= other; } +}; + +#define VALUE .val() + +#if USE_OPERATORS + +inline CPos operator+(const CPos& pos, COff off) +{ + int val = pos.value + off.value; + while (val >= pos.isize()) + val -= pos.isize(); + return CPos(pos.psize, val); +} + +inline CPos operator-(const CPos& pos, COff off) +{ + int val = pos.value - off.value; + while (val < 0) + val += pos.isize(); + return CPos(pos.psize, val); +} + +// Should verify that CPos use the same size! +inline COff operator-(CPos later, CPos earlier) +{ + if (later.value < earlier.value) + return COff(later.value + later.isize() - earlier.value); + + return COff(later.value - earlier.value); +} + +inline CSeqNo operator+(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::incseq(seq.val(), off.val()); + return CSeqNo(val); +} + +inline CSeqNo operator-(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::decseq(seq.val(), off.val()); + return CSeqNo(val); +} + + +#endif +const size_t fix_rollover = 16; +const CPos CPos_TRAP (&fix_rollover, -1); + +#else +#define VALUE +typedef int CPos; +typedef int COff; +const int CPos_TRAP = -1; +#endif + // // Circular receiver buffer. // @@ -229,9 +381,9 @@ class CRcvBuffer // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. - int first_seq; // sequence of the first available readable packet + CSeqNo first_seq; // sequence of the first available readable packet time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. - int avail_range; + COff avail_range; InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, time_point fp_time = time_point()) @@ -265,8 +417,8 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(int pos, int& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -289,7 +441,7 @@ class CRcvBuffer /// /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` /// - void updateGapInfo(int prev_max_pos); + void updateGapInfo(CPos prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -362,31 +514,32 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo; } + int getStartSeqNo() const { return m_iStartSeqNo VALUE; } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; } + void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. - size_t getAvailSize(int iFirstUnackSeqNo) const + size_t getAvailSize(int32_t iFirstUnackSeqNo) const { // Receiver buffer allows reading unacknowledged packets. // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const int iRBufSeqNo = getStartSeqNo(); - if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + const CSeqNo iRBufSeqNo = m_iStartSeqNo; + //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. @@ -436,7 +589,7 @@ class CRcvBuffer bool empty() const { - return (m_iMaxPosOff == 0); + return (m_iMaxPosOff == COff(0)); } /// Return buffer capacity. @@ -453,7 +606,7 @@ class CRcvBuffer /// between the initial position and the youngest received packet. size_t size() const { - return m_iMaxPosOff; + return size_t(m_iMaxPosOff VALUE); } // Returns true if the buffer is full. Requires locking. @@ -489,6 +642,7 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: + /* inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } @@ -506,20 +660,21 @@ class CRcvBuffer return off2 - off1; } + */ // NOTE: Assumes that pUnit != NULL - CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } - const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } + CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(CPos pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); void updateNonreadPos(); - void releaseUnitInPos(int pos); + void releaseUnitInPos(CPos pos); /// @brief Drop a unit from the buffer. /// @param pos position in the m_entries of the unit to drop. /// @return false if nothing to drop, true if the unit was dropped successfully. - bool dropUnitInPos(int pos); + bool dropUnitInPos(CPos pos); /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). @@ -528,15 +683,15 @@ class CRcvBuffer bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } /// Find position of the last packet of the message. - int findLastMessagePkt(); + CPos findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNonOrderPacket(int insertpos); + void onInsertNonOrderPacket(CPos insertpos); // Check if m_iFirstNonOrderMsgPos is still readable. bool checkFirstReadableNonOrder(); void updateFirstReadableNonOrder(); - int scanNonOrderMessageRight(int startPos, int msgNo) const; - int scanNonOrderMessageLeft(int startPos, int msgNo) const; + CPos scanNonOrderMessageRight(CPos startPos, int msgNo) const; + CPos scanNonOrderMessageLeft(CPos startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -588,18 +743,18 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - typedef FixedArray entries_t; + typedef FixedArray entries_t; entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue - int m_iStartSeqNo; - int m_iStartPos; // the head position for I/O (inclusive) - int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet - int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosOff; // the furthest data position + CSeqNo m_iStartSeqNo; + CPos m_iStartPos; // the head position for I/O (inclusive) + CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false @@ -607,7 +762,7 @@ class CRcvBuffer /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstNonOrderMsgPos; + CPos m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -637,7 +792,7 @@ class CRcvBuffer /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. - std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + std::string strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const; private: CTsbpdTime m_tsbpd; diff --git a/srtcore/common.h b/srtcore/common.h index 6a8912118..1c15dd01a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -580,6 +580,8 @@ class CSeqNo explicit CSeqNo(int32_t v): value(v) {} + int32_t val() const { return value; } + // Comparison bool operator == (const CSeqNo& other) const { return other.value == value; } bool operator < (const CSeqNo& other) const diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 45aeb2b4c..ad42ceb92 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8192,24 +8192,12 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); - if (m_bTsbPd) - { - /* - There's no need to update TSBPD in the wake-on-recv state - from ACK because it is being done already in the receiver thread - when a newly inserted packet caused provision of a new candidate - that could be delivered soon. Also, this flag is only used in TSBPD - mode and can be only set to true in the TSBPD thread. - - // Newly acknowledged data, signal TsbPD thread // - CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread - if (m_bWakeOnRecv) - tslcc.notify_one(); - - */ - } - else + // There's no need to update TSBPD in the wake-on-recv state + // from ACK because it is being done already in the receiver thread + // when a newly inserted packet caused provision of a new candidate + // that could be delivered soon. Also, this flag is only used in TSBPD + // mode and can be only set to true in the TSBPD thread. + if (!m_bTsbPd) { { CUniqueSync rdcc (m_RecvLock, m_RecvDataCond); @@ -10682,14 +10670,6 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); - - // XXX unsure as to whether this should change anything in the TSBPD conditions. - // Might be that this trigger is not necessary. - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } } // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 8a1374eb7..9dbebe762 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -414,7 +414,7 @@ struct DynamicStruct /// Fixed-size array template class. namespace srt { -template +template class FixedArray { public: @@ -430,22 +430,23 @@ class FixedArray } public: - const T& operator[](size_t index) const + const T& operator[](Indexer index) const { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } - T& operator[](size_t index) + T& operator[](Indexer index) { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } + /* const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) @@ -461,6 +462,7 @@ class FixedArray return m_entries[index]; } + */ size_t size() const { return m_size; } From ab469d65d58a9e4ec825d4557bc405f70e8abb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 10 Jun 2024 11:09:06 +0200 Subject: [PATCH 096/174] Fixed previous problems with receiver buffer --- srtcore/buffer_rcv.cpp | 168 +++++++++++++++++++---------------------- srtcore/buffer_rcv.h | 61 ++++++++++++--- 2 files changed, 129 insertions(+), 100 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ab72670df..539c51f9e 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,23 +75,6 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. - // The right edge is included because we expect iFirstNonreadPos to be - // right after the last valid packet position if all packets are available. - bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) - { - if (iFirstNonreadPos == iStartPos) - return true; - - //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; - const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; - - if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; - } } @@ -194,9 +177,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); + SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); - const CPos newpktpos = m_iStartPos + offset; + //const CPos newpktpos = m_iStartPos + offset; + const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) @@ -287,8 +271,8 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - //w_span = offPos(m_iStartPos, m_iEndPos); - w_span = m_iEndPos - m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = m_iEndPos - m_iStartPos; } if (pos == CPos_TRAP) @@ -312,8 +296,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p { time_point earlier_time; - //int prev_max_pos = incPos(m_iStartPos, prev_max_off); - CPos prev_max_pos = m_iStartPos + prev_max_off; + CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); + //CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -328,8 +312,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iEndPos = m_iStartPos + m_iMaxPosOff; + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -337,8 +321,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -483,12 +467,12 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); @@ -533,13 +517,13 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro COff minDroppedOffset = COff(-1); int iDropCnt = 0; const COff start_off = COff(max(0, offset_a)); - //const int start_pos = incPos(m_iStartPos, start_off); - const CPos start_pos = m_iStartPos + start_off; + const CPos start_pos = incPos(m_iStartPos, start_off); + //const CPos start_pos = m_iStartPos + start_off; const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); - //const int end_pos = incPos(m_iStartPos, end_off); - const CPos end_pos = m_iStartPos + end_off; + const CPos end_pos = incPos(m_iStartPos, end_off); + //const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (CPos i = start_pos; i != end_pos; ++i) + for (CPos i = start_pos; i != end_pos; i = incPos(i)) // ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -577,8 +561,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -588,8 +572,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - //const int stop_pos = decPos(m_iStartPos); - const CPos stop_pos = m_iStartPos - COff(1); + const CPos stop_pos = decPos(m_iStartPos); + //const CPos stop_pos = m_iStartPos - COff(1); for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. @@ -613,8 +597,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -660,8 +644,8 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - //int end_off = offPos(m_iStartPos, m_iEndPos); - COff end_off = m_iEndPos - m_iStartPos; + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; @@ -745,7 +729,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - //p = incPos(p); - ++p; + p = incPos(p); + //++p; m_iNotch = 0; m_iStartPos = p; @@ -954,7 +939,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -986,8 +971,8 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - //return offPos(m_iStartPos, m_iFirstNonreadPos); - return (m_iFirstNonreadPos - m_iStartPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + //return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -998,8 +983,9 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); - CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + CPos lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -1007,8 +993,8 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - //lastpos = decPos(lastpos); - --lastpos; + lastpos = decPos(lastpos); + //--lastpos; } if (m_entries[lastpos].pUnit == NULL) @@ -1017,8 +1003,8 @@ int CRcvBuffer::getTimespan_ms() const CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - //startpos = incPos(startpos); - ++startpos; + startpos = incPos(startpos); + //++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1077,9 +1063,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; - const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; return std::pair(m_iStartSeqNo VALUE, seqno_last); } @@ -1201,8 +1188,8 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. - const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. + const CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) @@ -1224,8 +1211,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - //m_iFirstNonreadPos = incPos(i); - m_iFirstNonreadPos = i + COff(1); + m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = i + COff(1); break; } } @@ -1301,10 +1288,10 @@ bool CRcvBuffer::checkFirstReadableNonOrder() if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); - const CPos endPos = m_iStartPos + m_iMaxPosOff; + const CPos endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) // ++pos) { if (!m_entries[pos].pUnit) return false; @@ -1339,7 +1326,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); CPos posFirst = CPos_TRAP; CPos posLast = CPos_TRAP; @@ -1397,7 +1385,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (startPos == lastPos) return CPos_TRAP; @@ -1508,7 +1497,8 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1572,8 +1562,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - //int frompos = incPos(m_iStartPos, offset); - CPos frompos = m_iStartPos + offset; + CPos frompos = incPos(m_iStartPos, offset); + //CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1582,15 +1572,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); COff ret_off = m_iMaxPosOff; - - COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } @@ -1605,8 +1595,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - //int pos = incPos(m_iStartPos, offset); - const CPos pos = m_iStartPos + offset; + const CPos pos = incPos(m_iStartPos, offset); + //const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; @@ -1632,11 +1622,11 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // points to an empty cell. for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - //int pos = incPos(m_iStartPos, off); - const CPos pos = m_iStartPos + off; + const CPos pos = incPos(m_iStartPos, off); + //const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; return ret_seq VALUE; } @@ -1645,7 +1635,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Fallback - this should be impossible, so issue a log. LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off) VALUE + << (m_iStartSeqNo + ret_off VALUE) VALUE << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 18450947d..ff659a45c 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -18,7 +18,7 @@ #include "utilities.h" #define USE_WRAPPERS 1 -#define USE_OPERATORS 1 +#define USE_OPERATORS 0 namespace srt { @@ -33,7 +33,7 @@ struct CPos int isize() const {return *psize;} - explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} @@ -642,25 +642,64 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: - /* - inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } - inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } - inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + //* + inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } + inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } + inline COff offPos(CPos pos1, CPos pos2) const + { + int diff = pos2 VALUE - pos1 VALUE; + if (diff >= 0) + { + return COff(diff); + } + return COff(m_szSize + diff); + } + + inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. /// @param pos1 a position in the receiver buffer. /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. - inline int cmpPos(int pos2, int pos1) const + inline COff cmpPos(CPos pos2, CPos pos1) const { // XXX maybe not the best implementation, but this keeps up to the rule. // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. - const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; - const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; - return off2 - off1; + return posToOff(pos2) - posToOff(pos1); + } + // */ + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + static bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + //const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + + if (isOverrun) + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + } + + bool isInUsedRange(CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == m_iStartPos) + return true; + + // DECODE the iFirstNonreadPos + int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + if (diff < 0) + diff += m_szSize; + + return diff <= m_iMaxPosOff VALUE; } - */ // NOTE: Assumes that pUnit != NULL CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } From a2b1b44b35c883ac176b5273856d1224bc5af089 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 17 Jun 2024 11:40:52 +0200 Subject: [PATCH 097/174] First working version with end/drop as offset --- CMakeLists.txt | 2 +- srtcore/buffer_rcv.cpp | 611 +++++++++++++++++++++------------------ srtcore/buffer_rcv.h | 123 +++++--- test/test_buffer_rcv.cpp | 71 +++++ testing/testmedia.cpp | 24 +- 5 files changed, 493 insertions(+), 338 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5994d3b7..70e1f50a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1528,7 +1528,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - gtest_discover_tests(test-srt) + #gtest_discover_tests(test-srt) endif() enable_testing() diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 539c51f9e..1562d0264 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -104,14 +104,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(&m_szSize, 0) - , m_iEndPos(&m_szSize, 0) - , m_iDropPos(&m_szSize, 0) - , m_iFirstNonreadPos(&m_szSize, 0) + , m_iStartPos(0) + , m_iEndOff(0) + , m_iDropOff(0) + , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) + , m_iFirstNonOrderMsgPos(CPos_TRAP) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -138,8 +138,8 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos VALUE - << " end=" << m_iEndPos VALUE - << " drop=" << m_iDropPos VALUE + << " end=+" << m_iEndOff VALUE + << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE << " seq[start]=%" << m_iStartSeqNo VALUE); } @@ -211,7 +211,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); + time_point earlier_time = updatePosInfo(unit, prev_max_off, offset, extended_end); InsertInfo ireport (InsertInfo::INSERTED); ireport.first_time = earlier_time; @@ -237,102 +237,103 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - CPos fallback_pos = CPos_TRAP; - if (!m_tsbpd.isEnabled()) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - // NOTE THAT this field can as well be -1 already. - fallback_pos = m_iFirstNonOrderMsgPos; - } - else if (m_iDropPos != m_iEndPos) - { - // With TSBPD regard the drop position (regardless if - // TLPKTDROP is currently on or off), if "exists", that - // is, m_iDropPos != m_iEndPos. - fallback_pos = m_iDropPos; - } - - // This finds the first possible available packet, which is - // preferably at cell 0, but if not available, try also with - // given fallback position (unless it's -1). - const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); - if (pkt) - { - w_if.first_seq = CSeqNo(pkt->getSeqNo()); - } -} - + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position, if it's set + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + const CPacket* pkt = &packetAt(m_iStartPos); + SRT_ASSERT(pkt); + w_if.avail_range = m_iEndOff; + w_if.first_seq = CSeqNo(pkt->getSeqNo()); + return; + } -const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) -{ - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); - //w_span = m_iEndPos - m_iStartPos; - } + // If not the first position, probe the skipped positions: + // - for live mode, check the DROP position + // (for potential after-drop reading) + // - for message mode, check the non-order message position + // (for potential out-of-oder message delivery) - if (pos == CPos_TRAP) - { - w_span = COff(0); - return NULL; - } + const CPacket* pkt = NULL; + if (m_tsbpd.isEnabled()) + { + // With TSBPD you can rely on drop position, if set + if (m_iDropOff) + { + pkt = &packetAt(incPos(m_iStartPos, m_iDropOff)); + SRT_ASSERT(pkt); + } + } + else + { + // Message-mode: try non-order read position. + if (m_iFirstNonOrderMsgPos != CPos_TRAP) + { + pkt = &packetAt(m_iFirstNonOrderMsgPos); + SRT_ASSERT(pkt); + } + } - SRT_ASSERT(m_entries[pos].pUnit != NULL); + if (!pkt) + { + // This is default, but set just in case + // The default seq is SRT_SEQNO_NONE. + w_if.avail_range = COff(0); + return; + } - // TODO: we know that at least 1 packet is available, but only - // with m_iEndPos we know where the true range is. This could also - // be implemented for message mode, but still this would employ - // a separate begin-end range declared for a complete out-of-order - // message. - w_span = COff(1); - return &packetAt(pos); + // TODO: we know that at least 1 packet is available, but only + // with m_iEndOff we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_if.avail_range = COff(1); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) + +// This function is called exclusively after packet insertion. +// This will update also m_iEndOff and m_iDropOff fields (the latter +// regardless of the TSBPD mode). +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, + const COff offset, + const bool extended_end) { time_point earlier_time; - CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); - //CPos prev_max_pos = m_iStartPos + prev_max_off; - // Update flags // Case [A] if (extended_end) { // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) + if (m_iEndOff == prev_max_off) { // THIS means that the new packet didn't CAUSE a gap if (m_iMaxPosOff == prev_max_off + 1) { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, + // This means that m_iEndOff now shifts by 1, + // and m_iDropOff must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - //m_iEndPos = m_iStartPos + m_iMaxPosOff; - m_iDropPos = m_iEndPos; + m_iEndOff = m_iMaxPosOff; + m_iDropOff = 0; } else { // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + // Therefore m_iEndOff STAYS WHERE IT IS. + m_iDropOff = m_iMaxPosOff - 1; } } } // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + // Since this place, every 'offset' is in the range + // between m_iEndOff (inclusive) and m_iMaxPosOff. // Here you can use prev_max_pos as the position represented // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) + else if (offset == m_iEndOff) { // Case [D]: inserted a packet at the first gap following the // contiguous region. This makes a potential to extend the @@ -342,22 +343,14 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // new earliest packet now. In any other situation under this // condition there's some contiguous packet range preceding // this position. - if (m_iEndPos == m_iStartPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } - updateGapInfo(prev_max_pos); + updateGapInfo(); } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - - //else if (cmpPos(newpktpos, m_iDropPos) < 0) - else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) + else if (offset < m_iDropOff) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -370,10 +363,10 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // // We know it because if the position has filled a gap following // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a + // by m_iDropOff, or it would point to some earlier packet in a // contiguous series of valid packets following a gap, hence // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; + m_iDropOff = offset; // If there's an inserted packet BEFORE drop-pos (which makes it // a new drop-pos), while the very first packet is absent (the @@ -382,7 +375,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position, but still following some earlier contiguous range // of valid packets - so it's earlier than previous drop, but // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } @@ -392,37 +385,94 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p return earlier_time; } -void CRcvBuffer::updateGapInfo(CPos prev_max_pos) +// This function is called when the m_iEndOff has been set to a new +// position and the m_iDropOff should be calculated since that position again. +void CRcvBuffer::updateGapInfo() { - CPos pos = m_iEndPos; + COff from = m_iEndOff, to = m_iMaxPosOff; + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); - // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + CPos pos = incPos(m_iStartPos, from); + + if (m_entries[pos].status == EntryState_Avail) { - if (m_entries[pos].status == EntryState_Empty) + + CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); + + for (; pos != end_pos; pos = incPos(pos)) { - break; + if (m_entries[pos].status != EntryState_Avail) + break; } + + m_iEndOff = offPos(m_iStartPos, pos); } - if (pos == prev_max_pos) + + // XXX This should be this way, but there are still inconsistencies + // in the message code. + //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); + + // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP + // is set. This option is not handled in message mode, only in live mode. + // Dropping by packet makes sense only in case of packetwise reading, + // which isn't the case of neither stream nor message mode. + if (!m_tsbpd.isEnabled()) { - // Reached the end and found no gaps. - m_iEndPos = prev_max_pos; - m_iDropPos = prev_max_pos; + m_iDropOff = 0; + return; } - else + + // Do not touch m_iDropOff if it's still beside the contiguous + // region. DO NOT SEARCH for m_iDropOff if m_iEndOff is max + // because this means that the whole buffer is contiguous. + // That would simply find nothing and only uselessly burden the + // performance by searching for a not present empty cell. + + // Also check if the current drop position is a readable packet. + // If not, start over. + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + + if (m_iDropOff < m_iEndOff || m_entries[drop_pos].status != EntryState_Avail) { - // Found a gap at pos - m_iEndPos = pos; - m_iDropPos = pos; // fallback, although SHOULD be impossible - // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + m_iDropOff = 0; + if (m_iEndOff < m_iMaxPosOff) { - if (m_entries[pos].status != EntryState_Empty) + int maxend = m_szSize - m_iStartPos VALUE; + int ifrom = m_iEndOff + 1; + int ito = m_iMaxPosOff VALUE; + + bool found = false; + for (int i = ifrom; i < std::min(maxend, ito); ++i) { - m_iDropPos = pos; - break; + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } } + + if (!found && ito > maxend) + { + int upto = ito - maxend; + for (int i = 0; i < upto; ++i) + { + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } + } + } + + // Must be found somewhere, worst case at the position + // of m_iMaxPosOff-1. If no finding loop caught it somehow, + // it will remain at 0. The case when you have empty packets + // in the busy range is only with message mode after reading + // packets out-of-order, but this doesn't use tsbpd mode. + SRT_ASSERT(m_iDropOff != 0); } } } @@ -444,9 +494,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosOff -= len; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + m_iMaxPosOff = decOff(m_iMaxPosOff, len); + m_iEndOff = decOff(m_iEndOff, len); + m_iDropOff = decOff(m_iDropOff, len); const int iDropCnt = len VALUE; while (len VALUE > 0) @@ -454,8 +504,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - //m_iStartPos = incPos(m_iStartPos); - ++m_iStartPos; + m_iStartPos = incPos(m_iStartPos); + //++m_iStartPos; --len; } @@ -465,10 +515,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); - // Start from here and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - //updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -538,7 +585,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (bKeepExisting && bnd == PB_SOLO) { bDropByMsgNo = false; // Solo packet, don't search for the rest of the message. - LOGC(rbuflog.Debug, + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %" << packetAt(i).getSeqNo() << "."); continue; @@ -565,6 +612,14 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro //minDroppedOffset = i - m_iStartPos; } + if (end_off > m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " - updating the busy region"); + m_iMaxPosOff = end_off; + } + if (bDropByMsgNo) { // If msgno is specified, potentially not the whole message was dropped using seqno range. @@ -574,7 +629,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // Try to drop by the message number in case the message starts earlier than @a seqnolo. const CPos stop_pos = decPos(m_iStartPos); //const CPos stop_pos = m_iStartPos - COff(1); - for (CPos i = start_pos; i != stop_pos; --i) + for (CPos i = start_pos; i != stop_pos; i = decPos(i)) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -607,14 +662,23 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); } + if (iDropCnt) + { + // We don't need the drop position, if we allow to drop messages by number + // and with that value we risk that drop was pointing to a dropped packet. + // Theoretically to make it consistent we need to shift the value to the + // next found packet, but we don't need this information if we use the message + // mode (because drop-by-packet is not supported in this mode) and this + // will burden the performance for nothing. + m_iDropOff = 0; + } + // Check if units before m_iFirstNonreadPos are dropped. const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); - // XXX TEST AND FIX - // Start from the last updated start pos and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(end_pos); + updateGapInfo(); + IF_HEAVY_LOGGING(debugShowState( ("dropmsg off %" + Sprint(seqnolo) + " #" + Sprint(msgno)).c_str())); @@ -636,7 +700,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const { - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); @@ -644,18 +708,15 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - - //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); - w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; + w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); - return (end_off < m_iMaxPosOff); + return (m_iEndOff < m_iMaxPosOff); } int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) @@ -681,7 +742,17 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); m_iStartSeqNo = CSeqNo(pktseqno) + 1; + ++nskipped; } else { @@ -759,72 +814,39 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); + + m_iEndOff = decOff(m_iEndOff, len); + } countBytes(-pkts_read, -bytes_extracted); releaseNextFillerEntries(); + // This will update the end position + updateGapInfo(); + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } - // Now that we have m_iStartPos potentially shifted, reinitialize - // m_iEndPos and m_iDropPos. - - CPos pend_pos = incPos(m_iStartPos, m_iMaxPosOff); - //CPos pend_pos = m_iStartPos + m_iMaxPosOff; - - // First check: is anything in the beginning - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - // If so, shift m_iEndPos up to the first nonexistent unit - // XXX Try to optimize search by splitting into two loops if necessary. - - m_iEndPos = incPos(m_iStartPos); - //m_iEndPos = m_iStartPos + COff(1); - while (m_entries[m_iEndPos].status == EntryState_Avail) - { - m_iEndPos = incPos(m_iEndPos); - //m_iEndPos = m_iEndPos + COff(1); - if (m_iEndPos == pend_pos) - break; - } - - // If we had first packet available, then there's also no drop pos. - m_iDropPos = m_iEndPos; - } - else - { - // If not, reset m_iEndPos and search for the first after-drop candidate. - m_iEndPos = m_iStartPos; - m_iDropPos = m_iEndPos; - - // The container could have become empty. - // Stay here if so. - if (m_iStartPos != pend_pos) - { - while (m_entries[m_iDropPos].status != EntryState_Avail) - { - m_iDropPos = incPos(m_iDropPos); - //m_iDropPos = m_iDropPos + COff(1); - if (m_iDropPos == pend_pos) - { - // Nothing found - set drop pos equal to end pos, - // which means there's no drop - m_iDropPos = m_iEndPos; - break; - } - } - } - } - - if (!m_tsbpd.isEnabled()) // We need updateFirstReadableNonOrder() here even if we are reading inorder, // incase readable inorder packets are all read out. @@ -923,6 +945,15 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + + --m_iEndOff; + if (m_iEndOff < 0) + m_iEndOff = 0; + + --m_iDropOff; + if (m_iDropOff < 0) + m_iDropOff = 0; + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; } @@ -1043,10 +1074,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - else if (m_iDropPos != m_iEndPos) + else if (m_iDropOff) { - SRT_ASSERT(m_entries[m_iDropPos].pUnit); - pkt = &packetAt(m_iDropPos); + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + SRT_ASSERT(m_entries[drop_pos].pUnit); + pkt = &packetAt(drop_pos); pi.seq_gap = true; // Available, but after a drop. } else @@ -1165,21 +1197,43 @@ bool CRcvBuffer::dropUnitInPos(CPos pos) return true; } -void CRcvBuffer::releaseNextFillerEntries() +int CRcvBuffer::releaseNextFillerEntries() { CPos pos = m_iStartPos; + int nskipped = 0; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { + if (nskipped == m_iMaxPosOff) + { + // This should never happen. All the previously read- or drop-marked + // packets should be contained in the range up to m_iMaxPosOff. Do not + // let the buffer ride any further and report the problem. Still stay there. + LOGC(rbuflog.Error, log << "releaseNextFillerEntries: IPE: Read/Drop status outside the busy range!"); + break; + } + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; releaseUnitInPos(pos); - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; m_iStartPos = pos; - --m_iMaxPosOff; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + ++nskipped; } + + if (!nskipped) + { + return nskipped; + } + + m_iMaxPosOff -= nskipped; + m_iEndOff = decOff(m_iEndOff, nskipped); + + // Drop off will be updated after that call, if needed. + m_iDropOff = 0; + + return nskipped; } // TODO: Is this function complete? There are some comments left inside. @@ -1197,7 +1251,7 @@ void CRcvBuffer::updateNonreadPos() if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) + for (CPos i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1226,7 +1280,7 @@ void CRcvBuffer::updateNonreadPos() CPos CRcvBuffer::findLastMessagePkt() { - for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1333,7 +1387,7 @@ void CRcvBuffer::updateFirstReadableNonOrder() CPos posLast = CPos_TRAP; int msgNo = -1; - for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) { if (!m_entries[pos].pUnit) { @@ -1393,8 +1447,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; if (!m_entries[pos].pUnit) break; @@ -1424,8 +1478,8 @@ CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = decPos(pos); - --pos; + pos = decPos(pos); + //--pos; if (!m_entries[pos].pUnit) return CPos_TRAP; @@ -1549,100 +1603,89 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { + // This means that there are no lost seqs at all, no matter + // from which position they would have to be checked. + if (m_iEndOff == m_iMaxPosOff) + return SRT_SEQNO_NONE; + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; COff offset (offset_val); - // Check if it's still inside the buffer - if (offset_val < 0 || offset >= m_iMaxPosOff) + // Check if it's still inside the buffer. + // Skip the region from 0 to m_iEndOff because this + // region is by definition contiguous and contains no loss. + if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } - // Start position - CPos frompos = incPos(m_iStartPos, offset); - //CPos frompos = m_iStartPos + offset; - - // Ok; likely we should stand at the m_iEndPos position. - // If this given position is earlier than this, then - // m_iEnd stands on the first loss, unless it's equal - // to the position pointed by m_iMaxPosOff. - - CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); - COff ret_off = m_iMaxPosOff; - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - if (offset < end_off) + // Check if this offset is equal to m_iEndOff. If it is, + // then you have the loss sequence exactly the one that + // was passed. Skip now, pw_end was not requested. + if (offset == m_iEndOff) { - // If m_iEndPos has such a value, then there are - // no loss packets at all. - if (end_off != m_iMaxPosOff) + if (pw_end) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); - ret_seq = m_iStartSeqNo + end_off VALUE; - ret_off = end_off; + // If the offset is exactly at m_iEndOff, then + // m_iDropOff will mark the end of gap. + if (m_iDropOff) + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + else + { + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); + *pw_end = fromseq; + } } + return fromseq; } - else - { - // Could be strange, but just as the caller wishes: - // find the first loss since this point on - // You can't rely on m_iEndPos, you are beyond that now. - // So simply find the next hole. - // REUSE offset as a control variable - for (; offset < m_iMaxPosOff; ++offset) + int ret_seq = SRT_SEQNO_NONE; + int loss_off = 0; + // Now find the first empty position since here, + // up to m_iMaxPosOff. Checking against m_iDropOff + // makes no sense because if it is not 0, you'll + // find it earlier by checking packet presence. + for (int off = offset; off < m_iMaxPosOff; ++off) + { + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status == EntryState_Empty) { - const CPos pos = incPos(m_iStartPos, offset); - //const CPos pos = m_iStartPos + offset; - if (m_entries[pos].status == EntryState_Empty) - { - ret_off = offset; - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); - ret_seq = m_iStartSeqNo + offset VALUE; - break; - } + ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + loss_off = off; + break; } } - // If found no loss, just return this value and do not - // rewrite nor look for anything. - - // Also no need to search anything if only the beginning was - // being looked for. - if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) - return ret_seq VALUE; - - // We want also the end range, so continue from where you - // stopped. + if (ret_seq == SRT_SEQNO_NONE) + { + // This is theoretically possible if we search from behind m_iEndOff, + // after m_iDropOff. This simply means that we are trying to search + // behind the last gap in the buffer. + return ret_seq; + } - // Start from ret_off + 1 because we know already that ret_off - // points to an empty cell. - for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) + // We get this position, so search for the end of gap + if (pw_end) { - const CPos pos = incPos(m_iStartPos, off); - //const CPos pos = m_iStartPos + off; - if (m_entries[pos].status != EntryState_Empty) + for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); - *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; - return ret_seq VALUE; + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + return ret_seq; + } } - } - // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" - //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off VALUE) VALUE - << " not followed by any valid cell"); - - // Return this in the last resort - this could only be a situation when - // a packet has somehow disappeared, but it contains empty cells up to the - // end of buffer occupied range. This shouldn't be possible at all because - // there must be a valid packet at least at the last occupied cell. - return SRT_SEQNO_NONE; + // Should not be possible to not find an existing packet + // following the gap, otherwise there would be no gap. + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: gap since %" << ret_seq << " not covered by existing packet"); + *pw_end = ret_seq; + } + return ret_seq; } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index ff659a45c..230efa481 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -29,22 +29,39 @@ namespace srt struct CPos { int value; +#if USE_OPERATORS const size_t* psize; - int isize() const {return *psize;} +#endif + +#if USE_OPERATORS + explicit CPos(const size_t* ps SRT_ATR_UNUSED, int val) + : value(val) + , psize(ps) + {} + +#else + explicit CPos(int val): value(val) {} +#endif - explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} - CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos(const CPos& src): value(src.value) +#if USE_OPERATORS + , psize(src.psize) +#endif + {} CPos& operator=(const CPos& src) { +#if USE_OPERATORS psize = src.psize; +#endif value = src.value; return *this; } +#if USE_OPERATORS int cmp(CPos other, CPos start) const { int pos2 = value; @@ -72,6 +89,7 @@ struct CPos value = 0; return *this; } +#endif bool operator == (CPos other) const { return value == other.value; } bool operator != (CPos other) const { return value != other.value; } @@ -116,6 +134,15 @@ struct COff bool operator > (int other) const { return value > other; } bool operator <= (int other) const { return value <= other; } bool operator >= (int other) const { return value >= other; } + + friend bool operator == (int value, COff that) { return value == that.value; } + friend bool operator != (int value, COff that) { return value != that.value; } + friend bool operator < (int value, COff that) { return value < that.value; } + friend bool operator > (int value, COff that) { return value > that.value; } + friend bool operator <= (int value, COff that) { return value <= that.value; } + friend bool operator >= (int value, COff that) { return value >= that.value; } + + operator bool() const { return value != 0; } }; #define VALUE .val() @@ -161,8 +188,7 @@ inline CSeqNo operator-(CSeqNo seq, COff off) #endif -const size_t fix_rollover = 16; -const CPos CPos_TRAP (&fix_rollover, -1); +const CPos CPos_TRAP (-1); #else #define VALUE @@ -174,22 +200,33 @@ const int CPos_TRAP = -1; // // Circular receiver buffer. // +// ICR = Initial Contiguous Region: all cells here contain valid packets +// SCRAP REGION: Region with possibly filled or empty cells +// NOTE: in scrap region, the first cell is empty and the last one filled. +// SPARE REGION: Region without packets +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ -// | | | | -// | | | \__last pkt received -// | | | -// | | \___ m_iDropPos -// | | -// | \___ m_iEndPos -// | -// \___ m_iStartPos: first packet position in the buffer -// -// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// | | | | +// | | | \__last pkt received +// |<------------->| m_iDropOff | +// | | | +// |<--------->| m_iEndOff | +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status: +// EntryState_Empty: No packet was ever received here +// EntryState_Avail: The packet is ready for reading +// EntryState_Read: The packet is non-order-read +// EntryState_Drop: The packet was requested to drop // // thread safety: // m_iStartPos: CUDT::m_RecvLock @@ -199,30 +236,30 @@ const int CPos_TRAP = -1; // // // m_iStartPos: the first packet that should be read (might be empty) -// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos -// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// m_iEndOff: shift to the end of contiguous range. This points always to an empty cell. +// m_iDropPos: shift a packet available for retrieval after a drop. If 0, no such packet. // // Operational rules: // // Initially: // m_iStartPos = 0 -// m_iEndPos = 0 -// m_iDropPos = 0 +// m_iEndOff = 0 +// m_iDropOff = 0 // // When a packet has arrived, then depending on where it landed: // // 1. Position: next to the last read one and newest // // m_iStartPos unchanged. -// m_iEndPos shifted by 1 -// m_iDropPos = m_iEndPos +// m_iEndOff shifted by 1 +// m_iDropOff = 0 // // 2. Position: after a loss, newest. // // m_iStartPos unchanged. -// m_iEndPos unchanged. -// m_iDropPos: -// - if it was == m_iEndPos, set to this +// m_iEndOff unchanged. +// m_iDropOff: +// - set to this packet, if m_iDropOff == 0 or m_iDropOff is past this packet // - otherwise unchanged // // 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos @@ -417,8 +454,7 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const COff offset, const bool extended_end); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -436,12 +472,7 @@ class CRcvBuffer /// update the m_iEndPos and m_iDropPos fields, or set them /// both to the end of range if there are no loss gaps. /// - /// The @a prev_max_pos parameter is passed here because it is already - /// calculated in insert(), otherwise it would have to be calculated here again. - /// - /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` - /// - void updateGapInfo(CPos prev_max_pos); + void updateGapInfo(); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -643,9 +674,9 @@ class CRcvBuffer private: //* - inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } - inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } - inline COff offPos(CPos pos1, CPos pos2) const + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } + CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + COff offPos(CPos pos1, CPos pos2) const { int diff = pos2 VALUE - pos1 VALUE; if (diff >= 0) @@ -655,7 +686,15 @@ class CRcvBuffer return COff(m_szSize + diff); } - inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + + static COff decOff(COff val, int shift) + { + int ival = val VALUE - shift; + if (ival < 0) + return COff(0); + return COff(ival); + } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. @@ -678,7 +717,7 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); //const CPos iLastPos = iStartPos + iMaxPosOff; const bool isOverrun = iLastPos VALUE < iStartPos VALUE; @@ -717,7 +756,9 @@ class CRcvBuffer /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). - void releaseNextFillerEntries(); + /// + /// @return the range for which the start pos has been shifted + int releaseNextFillerEntries(); bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } @@ -790,8 +831,8 @@ class CRcvBuffer CSeqNo m_iStartSeqNo; CPos m_iStartPos; // the head position for I/O (inclusive) - CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + COff m_iEndOff; // past-the-end of the contiguous region since m_iStartOff + COff m_iDropOff; // points past m_iEndOff to the first deliverable after a gap, or == m_iEndOff if no such packet CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 10be243a9..9436fa77d 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -646,6 +646,77 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } +TEST_F(CRcvBufferReadMsg, MsgOrderScraps) +{ + // Ok, in this test we're filling the message this way: + // 1. We have an empty packet in the first cell. + // 2. This is followed by a 5-packet message that is valid. + // 3. This is followed by empty, valid, empty, valid, valid packet, + // where all valid packets belong to the same message. + // 4. After that there should be 3-packet valid messsage. + // 5. We deploy drop request to that second scrapped message. + // 6. We read one message. Should be the first message. + // 7. We read one message. Should be the last message. + + auto& rcv_buffer = *m_rcv_buffer.get(); + + // 1, 2 + addMessage(5,// packets + 2, // msgno + m_init_seqno + 1, + true); + + // LAYOUT: 10 11 12 13 + // [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + // * (2 2 2 2 2) * 3 * 3 3) (4 4 4) + + // 3 + addPacket( + m_init_seqno + 7, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 9, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 10, + 3, + false, true, // last + true); + + // 4 + addMessage(3, // packets + 4, // msgno + m_init_seqno + 11, + true); + + // 5 + EXPECT_GT(rcv_buffer.dropMessage(m_init_seqno+8, m_init_seqno+8, 3, CRcvBuffer::KEEP_EXISTING), 0); + + // 6 + array buff; + SRT_MSGCTRL mc; + pair seqrange; + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 2); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); + + CRcvBuffer::InsertInfo ii; + rcv_buffer.getAvailInfo((ii)); + EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); + + // 7 + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 4); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); + +} + // One message (4 packets) is added to the buffer after a message with "in order" flag. // Read in order TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..1bf63ed64 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1059,8 +1059,8 @@ void SrtCommon::OpenGroupClient() if (!extras.empty()) { Verb() << "?" << extras[0] << VerbNoEOL; - for (size_t i = 1; i < extras.size(); ++i) - Verb() << "&" << extras[i] << VerbNoEOL; + for (size_t x = 1; x < extras.size(); ++x) + Verb() << "&" << extras[x] << VerbNoEOL; } Verb(); @@ -1130,15 +1130,15 @@ void SrtCommon::OpenGroupClient() // spread the setting on all sockets. ConfigurePost(m_sock); - for (size_t i = 0; i < targets.size(); ++i) + for (size_t x = 0; x < targets.size(); ++x) { // As m_group_nodes is simply transformed into 'targets', // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[x].id != -1 && targets[x].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[x].socket = targets[x].id; } } @@ -1159,12 +1159,12 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t x = 0; x < m_group_nodes.size(); ++x) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[x].socket; if (insock == -1) { - Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; + Verb() << "TARGET '" << sockaddr_any(targets[x].peeraddr).str() << "' connection failed."; continue; } @@ -1194,11 +1194,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int x = 0; x < len1; ++x) + Verb() << " " << ready_conn[x] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int x = 0; x < len2; ++x) + Verb() << " " << ready_err[x] << VerbNoEOL; Verb() << ""; From ff16e6871036a25622b47e92fe7867284cd7596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 17 Jun 2024 13:52:01 +0200 Subject: [PATCH 098/174] Blocked development support types and fixed the test --- srtcore/buffer_rcv.cpp | 32 ++++++++++++++++---------------- srtcore/buffer_rcv.h | 17 ++++++++++------- srtcore/common.h | 8 ++++++++ test/test_buffer_rcv.cpp | 4 ++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 1562d0264..494dc3763 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -141,7 +141,7 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) << " end=+" << m_iEndOff VALUE << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE - << " seq[start]=%" << m_iStartSeqNo VALUE); + << " seq[start]=%" << m_iStartSeqNo.val()); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) @@ -389,7 +389,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff, to = m_iMaxPosOff; + COff from = m_iEndOff; //, to = m_iMaxPosOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -536,8 +536,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; + const int32_t end_seqno = CSeqNo::incseq(m_iStartSeqNo.val(), m_iMaxPosOff); + //const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -556,7 +556,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo.val() << "."); return 0; } @@ -615,7 +615,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (end_off > m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi - << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo.val(), end_off) << " - updating the busy region"); m_iMaxPosOff = end_off; } @@ -703,17 +703,17 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); - w_seq = m_iStartSeqNo VALUE; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo.val()); + w_seq = m_iStartSeqNo.val(); return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE - << " base=%" << m_iStartSeqNo VALUE + << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); return (m_iEndOff < m_iMaxPosOff); @@ -1096,10 +1096,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo.val(), nonread_off VALUE); //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; - return std::pair(m_iStartSeqNo VALUE, seqno_last); + return std::pair(m_iStartSeqNo.val(), seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1539,7 +1539,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1632,7 +1632,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // If the offset is exactly at m_iEndOff, then // m_iDropOff will mark the end of gap. if (m_iDropOff) - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), m_iDropOff); else { LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); @@ -1653,7 +1653,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); loss_off = off; break; } @@ -1675,7 +1675,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); return ret_seq; } } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 230efa481..c843abcec 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -17,7 +17,7 @@ #include "tsbpd_time.h" #include "utilities.h" -#define USE_WRAPPERS 1 +#define USE_WRAPPERS 0 #define USE_OPERATORS 0 namespace srt @@ -25,6 +25,9 @@ namespace srt // DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) +// NOTE: This below series of definitions for CPos and COff +// are here for development support only, but they are not in +// use in the release code - there CPos and COff are aliases to int. #if USE_WRAPPERS struct CPos { @@ -545,11 +548,11 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo VALUE; } + int32_t getStartSeqNo() const { return m_iStartSeqNo.val(); } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } + void setStartSeqNo(int32_t seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. @@ -561,16 +564,16 @@ class CRcvBuffer // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const CSeqNo iRBufSeqNo = m_iStartSeqNo; - //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo - if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) + const int32_t iRBufSeqNo = m_iStartSeqNo.val(); + if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + //if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. diff --git a/srtcore/common.h b/srtcore/common.h index 1c15dd01a..e0d7212cc 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -685,14 +685,20 @@ class CSeqNo inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + CSeqNo inc() const { return CSeqNo(incseq(value)); } + inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + CSeqNo dec() const { return CSeqNo(decseq(value)); } + inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) + CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } + inline static int32_t decseq(int32_t seq, int32_t dec) { // Check if seq - dec < 0, but before it would have happened @@ -705,6 +711,8 @@ class CSeqNo return seq - dec; } + CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } + static int32_t maxseq(int32_t seq1, int32_t seq2) { if (seqcmp(seq1, seq2) < 0) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 9436fa77d..155dd7bdd 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -702,7 +702,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) array buff; SRT_MSGCTRL mc; pair seqrange; - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*5); EXPECT_EQ(mc.msgno, 2); EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); @@ -711,7 +711,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); // 7 - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*3); EXPECT_EQ(mc.msgno, 4); EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); From fa70fdaf0ee32ee8ae42a9c48d4c8cbe2c2afc24 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 12:55:09 +0200 Subject: [PATCH 099/174] Merged changes --- srtcore/buffer_rcv.cpp | 15 +++++++++++++++ srtcore/buffer_rcv.h | 12 ++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a323360bb..d790345a6 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -435,6 +435,20 @@ void CRcvBuffer::updateGapInfo() m_iDropOff = 0; if (m_iEndOff < m_iMaxPosOff) { + CPos start = incPos(m_iStartPos, m_iEndOff + 1), + end = incPos(m_iStartPos, m_iEndOff); + + for (CPos i = start; i != end; i = incPos(i)) + { + if (m_entries[i].status == EntryState_Avail) + { + m_iDropOff = offPos(m_iStartPos, i); + break; + } + } + + /* OPTIMIZED, but buggy. + int maxend = m_szSize - m_iStartPos VALUE; int ifrom = m_iEndOff + 1; int ito = m_iMaxPosOff VALUE; @@ -463,6 +477,7 @@ void CRcvBuffer::updateGapInfo() } } } + */ // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 7ad000b69..325245194 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -208,14 +208,14 @@ const int CPos_TRAP = -1; // NOTE: in scrap region, the first cell is empty and the last one filled. // SPARE REGION: Region without packets // -// | BUSY REGION | -// | | | | -// | ICR | SCRAP REGION | SPARE REGION...-> -// ......->| | | | -// | FIRST-GAP | | +// | BUSY REGION | +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | /FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ From 1bf93ff4afc1843c8716d159ebe1b4f8a0cbe7a4 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 15:33:09 +0200 Subject: [PATCH 100/174] Cleanup of the commented-out code --- srtcore/buffer_rcv.cpp | 56 +++++------------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index d790345a6..9e19884b7 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -148,7 +148,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -387,7 +386,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff; //, to = m_iMaxPosOff; + COff from = m_iEndOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -407,7 +406,7 @@ void CRcvBuffer::updateGapInfo() // XXX This should be this way, but there are still inconsistencies // in the message code. - //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + //USE: SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP @@ -447,38 +446,6 @@ void CRcvBuffer::updateGapInfo() } } - /* OPTIMIZED, but buggy. - - int maxend = m_szSize - m_iStartPos VALUE; - int ifrom = m_iEndOff + 1; - int ito = m_iMaxPosOff VALUE; - - bool found = false; - for (int i = ifrom; i < std::min(maxend, ito); ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - - if (!found && ito > maxend) - { - int upto = ito - maxend; - for (int i = 0; i < upto; ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - } - */ - // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, // it will remain at 0. The case when you have empty packets @@ -566,8 +533,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) @@ -624,7 +589,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); - //minDroppedOffset = i - m_iStartPos; } if (end_off > m_iMaxPosOff) @@ -812,7 +776,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - //const CPos newpktpos = m_iStartPos + offset; const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; @@ -193,7 +192,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -466,7 +465,6 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); - //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -479,7 +477,7 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) int iNumDropped = 0; // Number of dropped packets that were missing. int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. - while (len VALUE > 0) + while (len > 0) { // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. @@ -685,10 +683,10 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff); - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE - << " maxD=" << m_iMaxPosOff VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff + << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); @@ -917,7 +915,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + SRT_ASSERT(m_iMaxPosOff >= 0); m_iEndOff = decOff(m_iEndOff, 1); m_iDropOff = decOff(m_iDropOff, 1); @@ -943,8 +941,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE - << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -968,7 +966,7 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -1057,7 +1055,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const CSeqNo seqno_last = m_iStartSeqNo + nonread_off VALUE; + const CSeqNo seqno_last = m_iStartSeqNo + nonread_off; return std::pair(m_iStartSeqNo.val(), seqno_last.val()); } @@ -1488,7 +1486,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& stringstream ss; ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() - << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1555,17 +1553,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) if (m_iEndOff == m_iMaxPosOff) return SRT_SEQNO_NONE; - //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); - int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; - COff offset (offset_val); + COff offset = COff(CSeqNo(fromseq) - m_iStartSeqNo); // Check if it's still inside the buffer. // Skip the region from 0 to m_iEndOff because this // region is by definition contiguous and contains no loss. - if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) + if (offset < m_iEndOff || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq - << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } @@ -1597,7 +1593,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // find it earlier by checking packet presence. for (int off = offset; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); @@ -1619,7 +1615,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 325245194..ec5f2c256 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -148,8 +148,6 @@ struct COff operator bool() const { return value != 0; } }; -#define VALUE .val() - #if USE_OPERATORS inline CPos operator+(const CPos& pos, COff off) @@ -194,7 +192,6 @@ inline CSeqNo operator-(CSeqNo seq, COff off) const CPos CPos_TRAP (-1); #else -#define VALUE typedef int CPos; typedef int COff; const int CPos_TRAP = -1; @@ -679,11 +676,11 @@ class CRcvBuffer private: //* - CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } - CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos + inc) % m_szSize); } + CPos decPos(CPos pos) const { return (pos - 1) >= 0 ? CPos(pos - 1) : CPos(m_szSize - 1); } COff offPos(CPos pos1, CPos pos2) const { - int diff = pos2 VALUE - pos1 VALUE; + int diff = pos2 - pos1; if (diff >= 0) { return COff(diff); @@ -695,7 +692,7 @@ class CRcvBuffer static COff decOff(COff val, int shift) { - int ival = val VALUE - shift; + int ival = val - shift; if (ival < 0) return COff(0); return COff(ival); @@ -722,14 +719,13 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); - //const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + const CPos iLastPos = CPos((iStartPos + iMaxPosOff) % int(iSize)); + const bool isOverrun = iLastPos < iStartPos; if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; } bool isInUsedRange(CPos iFirstNonreadPos) @@ -738,11 +734,11 @@ class CRcvBuffer return true; // DECODE the iFirstNonreadPos - int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + int diff = iFirstNonreadPos - m_iStartPos; if (diff < 0) diff += m_szSize; - return diff <= m_iMaxPosOff VALUE; + return diff <= m_iMaxPosOff; } // NOTE: Assumes that pUnit != NULL From db0d8a18527265a978766c3cbfed2b5ec2b49795 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 19 Aug 2024 14:04:49 +0200 Subject: [PATCH 102/174] [core] Average values for sent/recv bitrate are now over last second. --- srtcore/buffer_tools.cpp | 43 +++++++++++++++++++++++++++++++++ srtcore/buffer_tools.h | 52 +++++++++++++++++++++++++++++++++++----- srtcore/core.cpp | 7 ++++++ srtcore/stats.h | 29 ++++++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 3f7a77be6..235766fc3 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -272,5 +272,48 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const return val; } +CMobileRateEstimator::CMobileRateEstimator() + : m_iCurSampleIdx(0) + , m_iRateKbps(0) +{ + resetMeasuresTable(); +} + +void CMobileRateEstimator::addSample(int pkts, double bytes) +{ + const time_point now = steady_clock::now(); + const int iSampleDeltaIdx = (int) count_milliseconds(now - lastTimestamp) / SAMPLE_DURATION_MS; + + if((m_iCurSampleIdx + iSampleDeltaIdx) < NUM_PERIODS) + resetMeasuresTable(m_iCurSampleIdx+1,m_iCurSampleIdx + iSampleDeltaIdx); + else { + int loopbackDiff = m_iCurSampleIdx + iSampleDeltaIdx - NUM_PERIODS; + resetMeasuresTable(m_iCurSampleIdx+1,NUM_PERIODS); + resetMeasuresTable(0,loopbackDiff); + } + + m_iCurSampleIdx = ((m_iCurSampleIdx + iSampleDeltaIdx) % NUM_PERIODS); + m_Samples[m_iCurSampleIdx].m_iBytesCount = bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount = pkts; + + lastTimestamp = now; + + computeAverageValueFromTable(); +} + +void CMobileRateEstimator::resetMeasuresTable(int from, int to) +{ + for(int i = max(0, from); i < min(int(NUM_PERIODS), to); i++) + m_Samples[i].reset(); +} + +void CMobileRateEstimator::computeAverageValueFromTable(){ + m_iRateKbps = 0; + + for(int i = 0; i < NUM_PERIODS; i++) + m_iRateKbps += m_Samples[i].m_iBytesCount; + + m_iRateKbps = m_iRateKbps * 8 / (NUM_PERIODS * SAMPLE_DURATION_MS); +} } diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index aacbd8310..fa35c6c8d 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -134,6 +134,7 @@ class CSndRateEstimator public: CSndRateEstimator(const time_point& tsNow); + CSndRateEstimator() {}; /// Add sample. /// @param [in] time sample (sending) time. @@ -147,9 +148,7 @@ class CSndRateEstimator /// Retrieve estimated bitrate in bytes per second inluding the current sampling interval. int getCurrentRate() const; -private: - static const int NUM_PERIODS = 10; - static const int SAMPLE_DURATION_MS = 100; // 100 ms +protected: struct Sample { int m_iPktsCount; // number of payload packets @@ -187,14 +186,55 @@ class CSndRateEstimator bool empty() const { return m_iPktsCount == 0; } }; - int incSampleIdx(int val, int inc = 1) const; - - Sample m_Samples[NUM_PERIODS]; +private: + static const int NUM_PERIODS = 10; + static const int SAMPLE_DURATION_MS = 100; // 100 ms time_point m_tsFirstSampleTime; //< Start time of the first sameple. int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. int m_iRateBps; // Input Rate in Bytes/sec + Sample m_Samples[NUM_PERIODS]; + + int incSampleIdx(int val, int inc = 1) const; + + + +}; + +class CMobileRateEstimator:CSndRateEstimator +{ + typedef sync::steady_clock::time_point time_point; + +public: + CMobileRateEstimator(); + + /// Add sample. + /// @param [in] pkts number of packets in the sample. + /// @param [in] bytes number of payload bytes in the sample. + void addSample(int pkts = 0, double bytes = 0); + + /// Clean the mobile measures table to reset average value. + void resetMeasuresTable() {resetMeasuresTable(0,NUM_PERIODS);}; + + /// Retrieve estimated bitrate in kilobits per second + int getRateKbps() const { return m_iRateKbps; } + +private: + static const int NUM_PERIODS = 100; // To get 1s of values + static const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point lastTimestamp; // Used to compute the delta between 2 calls + int m_iCurSampleIdx; // Index of the current sample being collected. + int m_iRateKbps; // The average value over the period (1s) in Kb + Sample m_Samples[NUM_PERIODS]; // Table of stored data + + /// This method will compute the average value based on all table's measures and the period (NUM_PERIODS*SAMPLE_DURATION_MS) + void computeAverageValueFromTable(); + + /// Reset a part of the stored measures + /// @param from The beginning where the reset have to be applied + /// @param to The last data that have to be reset + void resetMeasuresTable(int from, int to); }; } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7067e195c..6bfa76001 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7569,6 +7569,13 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); + + // Average values management + m_stats.sndr.fulfillMeasuresTable(perf->pktSent, double(perf->byteSent)); + m_stats.rcvr.fulfillMeasuresTable(perf->pktRecv, double(perf->byteRecv)); + perf->mbpsSendRate = m_stats.sndr.getAverageValueFromTable(); + perf->mbpsRecvRate = m_stats.rcvr.getAverageValueFromTable(); + // 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; diff --git a/srtcore/stats.h b/srtcore/stats.h index 947489eb1..4de3b9188 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -13,6 +13,7 @@ #include "platform_sys.h" #include "packet.h" +#include "buffer_tools.h" namespace srt { @@ -144,6 +145,8 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. + CMobileRateEstimator mobileRateEstimator; // The average Mbps over last second + void reset() { sent.reset(); @@ -167,6 +170,18 @@ struct Sender recvdNak.resetTrace(); sentFilterExtra.resetTrace(); } + + void fulfillMeasuresTable(int pkts, double bytes) { + mobileRateEstimator.addSample(pkts, bytes); + } + + void resetMeasuresTable() { + mobileRateEstimator.resetMeasuresTable(); + } + + int getAverageValueFromTable(){ + return mobileRateEstimator.getRateKbps(); + } }; /// Receiver-side statistics. @@ -187,6 +202,8 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. + CMobileRateEstimator mobileRateEstimator; // The average Mbps over last second + void reset() { recvd.reset(); @@ -218,6 +235,18 @@ struct Receiver sentAck.resetTrace(); sentNak.resetTrace(); } + + void fulfillMeasuresTable(int pkts, double bytes) { + mobileRateEstimator.addSample(pkts, bytes); + } + + void resetMeasuresTable() { + mobileRateEstimator.resetMeasuresTable(); + } + + int getAverageValueFromTable(){ + return mobileRateEstimator.getRateKbps(); + } }; } // namespace stats From 78b3412ca6d600792e33348dab40817c1c383883 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 19 Aug 2024 14:23:20 +0200 Subject: [PATCH 103/174] [core] Adding packet header size in average measure --- srtcore/buffer_tools.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index e9f15013b..f9d8a3d06 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -312,9 +312,9 @@ void CMobileRateEstimator::computeAverageValueFromTable(){ m_iRateKbps = 0; for(int i = 0; i < NUM_PERIODS; i++) - m_iRateKbps += m_Samples[i].m_iBytesCount; + m_iRateKbps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)) * 8; - m_iRateKbps = m_iRateKbps * 8 / (NUM_PERIODS * SAMPLE_DURATION_MS); + m_iRateKbps = m_iRateKbps / (NUM_PERIODS * SAMPLE_DURATION_MS); } } From cd42a9bed4b666e1d5c001e5e96421a31512eb94 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 19 Aug 2024 14:37:39 +0200 Subject: [PATCH 104/174] [core] Fixing auto merge removed methode --- srtcore/buffer_tools.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index 430b4a612..168b068b2 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -197,6 +197,8 @@ class CSndRateEstimator int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. int m_iRateBps; //< Rate in Bytes/sec. + + int incSampleIdx(int val, int inc = 1) const; }; class CMobileRateEstimator:CSndRateEstimator From ba4181505961187641e7d34c3f98ee78421075dc Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Thu, 22 Aug 2024 11:55:12 +0200 Subject: [PATCH 105/174] [core] Refactoring pull request --- srtcore/buffer_tools.cpp | 69 ++++++++++++++++++++++++---------------- srtcore/buffer_tools.h | 41 +++++++++++------------- srtcore/core.cpp | 8 ++--- srtcore/stats.h | 28 +++++----------- 4 files changed, 71 insertions(+), 75 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index f9d8a3d06..57e028f11 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -273,48 +273,61 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const return val; } -CMobileRateEstimator::CMobileRateEstimator() - : m_iCurSampleIdx(0) - , m_iRateKbps(0) +CMovingRateEstimator::CMovingRateEstimator() + : CSndRateEstimator(sync::steady_clock::now()) + , m_Samples(NUM_PERIODS) { - resetMeasuresTable(); + resetRate(0, NUM_PERIODS); } -void CMobileRateEstimator::addSample(int pkts, double bytes) +void CMovingRateEstimator::addSample(int pkts, double bytes) { - const time_point now = steady_clock::now(); - const int iSampleDeltaIdx = (int) count_milliseconds(now - lastTimestamp) / SAMPLE_DURATION_MS; - - if((m_iCurSampleIdx + iSampleDeltaIdx) < NUM_PERIODS) - resetMeasuresTable(m_iCurSampleIdx+1,m_iCurSampleIdx + iSampleDeltaIdx); - else { - int loopbackDiff = m_iCurSampleIdx + iSampleDeltaIdx - NUM_PERIODS; - resetMeasuresTable(m_iCurSampleIdx+1,NUM_PERIODS); - resetMeasuresTable(0,loopbackDiff); + const time_point now = steady_clock::now(); + const int iSampleDeltaIdx = (int)count_milliseconds(now - lastSlotTimestamp) / SAMPLE_DURATION_MS; + + if (iSampleDeltaIdx == 0) + { + m_Samples[m_iCurSampleIdx].m_iBytesCount += bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount += pkts; } + else + { + if ((m_iCurSampleIdx + iSampleDeltaIdx) < NUM_PERIODS) + resetRate(m_iCurSampleIdx + 1, m_iCurSampleIdx + iSampleDeltaIdx); + else + { + int loopbackDiff = m_iCurSampleIdx + iSampleDeltaIdx - NUM_PERIODS; + resetRate(m_iCurSampleIdx + 1, NUM_PERIODS); + resetRate(0, loopbackDiff); + } - m_iCurSampleIdx = ((m_iCurSampleIdx + iSampleDeltaIdx) % NUM_PERIODS); - m_Samples[m_iCurSampleIdx].m_iBytesCount = bytes; - m_Samples[m_iCurSampleIdx].m_iPktsCount = pkts; + m_iCurSampleIdx = ((m_iCurSampleIdx + iSampleDeltaIdx) % NUM_PERIODS); + m_Samples[m_iCurSampleIdx].m_iBytesCount = bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount = pkts; - lastTimestamp = now; + lastSlotTimestamp = now; + } - computeAverageValueFromTable(); + computeAverageValue(); } -void CMobileRateEstimator::resetMeasuresTable(int from, int to) +void CMovingRateEstimator::resetRate(int from, int to) { - for(int i = max(0, from); i < min(int(NUM_PERIODS), to); i++) + for (int i = max(0, from); i < min(int(NUM_PERIODS), to); i++) m_Samples[i].reset(); } -void CMobileRateEstimator::computeAverageValueFromTable(){ - m_iRateKbps = 0; +void CMovingRateEstimator::computeAverageValue() +{ + const time_point now = steady_clock::now(); + const int startDelta = count_milliseconds(now - m_tsFirstSampleTime); + const bool isFirstPeriod = startDelta < 1000; + m_iRateBps = 0; - for(int i = 0; i < NUM_PERIODS; i++) - m_iRateKbps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)) * 8; + for (int i = 0; i < NUM_PERIODS; i++) + m_iRateBps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)); - m_iRateKbps = m_iRateKbps / (NUM_PERIODS * SAMPLE_DURATION_MS); + if (isFirstPeriod) + m_iRateBps = m_iRateBps * 1000 / startDelta; } -} - +} // namespace srt diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index 168b068b2..dadafdd45 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -134,7 +134,6 @@ class CSndRateEstimator public: CSndRateEstimator(const time_point& tsNow); - CSndRateEstimator() {}; /// Add sample. /// @param [in] time sample (sending) time. @@ -150,6 +149,10 @@ class CSndRateEstimator int getCurrentRate() const; protected: + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; //< Rate in Bytes/sec. + time_point m_tsFirstSampleTime; //< Start time of the first sample. + struct Sample { int m_iPktsCount; // number of payload packets @@ -190,23 +193,18 @@ class CSndRateEstimator private: static const int NUM_PERIODS = 10; static const int SAMPLE_DURATION_MS = 100; // 100 ms - - Sample m_Samples[NUM_PERIODS]; - - time_point m_tsFirstSampleTime; //< Start time of the first sample. - int m_iFirstSampleIdx; //< Index of the first sample. - int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; //< Rate in Bytes/sec. + Sample m_Samples[NUM_PERIODS]; + int m_iFirstSampleIdx; //< Index of the first sample. int incSampleIdx(int val, int inc = 1) const; }; -class CMobileRateEstimator:CSndRateEstimator +class CMovingRateEstimator : CSndRateEstimator { typedef sync::steady_clock::time_point time_point; public: - CMobileRateEstimator(); + CMovingRateEstimator(); /// Add sample. /// @param [in] pkts number of packets in the sample. @@ -214,26 +212,25 @@ class CMobileRateEstimator:CSndRateEstimator void addSample(int pkts = 0, double bytes = 0); /// Clean the mobile measures table to reset average value. - void resetMeasuresTable() {resetMeasuresTable(0,NUM_PERIODS);}; + void resetRate() { resetRate(0, NUM_PERIODS); }; - /// Retrieve estimated bitrate in kilobits per second - int getRateKbps() const { return m_iRateKbps; } + /// Retrieve estimated bitrate in bytes per second with 16-byte packet header. + int getRate() const { return m_iRateBps; } private: - static const int NUM_PERIODS = 100; // To get 1s of values - static const int SAMPLE_DURATION_MS = 10; // 10 ms - time_point lastTimestamp; // Used to compute the delta between 2 calls - int m_iCurSampleIdx; // Index of the current sample being collected. - int m_iRateKbps; // The average value over the period (1s) in Kb - Sample m_Samples[NUM_PERIODS]; // Table of stored data + const int NUM_PERIODS = 100; // To get 1s of values + const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point lastSlotTimestamp; // Used to compute the delta between 2 calls + srt::FixedArray m_Samples; // Table of stored data - /// This method will compute the average value based on all table's measures and the period (NUM_PERIODS*SAMPLE_DURATION_MS) - void computeAverageValueFromTable(); + /// This method will compute the average value based on all table's measures and the period + /// (NUM_PERIODS*SAMPLE_DURATION_MS) + void computeAverageValue(); /// Reset a part of the stored measures /// @param from The beginning where the reset have to be applied /// @param to The last data that have to be reset - void resetMeasuresTable(int from, int to); + void resetRate(int from, int to); }; } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 16ecd70db..24d1879d5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7571,11 +7571,9 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) // Average values management - m_stats.sndr.fulfillMeasuresTable(perf->pktSent, double(perf->byteSent)); - m_stats.rcvr.fulfillMeasuresTable(perf->pktRecv, double(perf->byteRecv)); - perf->mbpsSendRate = m_stats.sndr.getAverageValueFromTable(); - perf->mbpsRecvRate = m_stats.rcvr.getAverageValueFromTable(); - + perf->mbpsSendRate = Bps2Mbps(m_stats.sndr.getAverageValue()); + perf->mbpsRecvRate = Bps2Mbps(m_stats.rcvr.getAverageValue()); + // 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; diff --git a/srtcore/stats.h b/srtcore/stats.h index 4de3b9188..16043fc35 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -145,7 +145,7 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - CMobileRateEstimator mobileRateEstimator; // The average Mbps over last second + CMovingRateEstimator mobileRateEstimator; // The average Mbps over last second void reset() { @@ -171,17 +171,11 @@ struct Sender sentFilterExtra.resetTrace(); } - void fulfillMeasuresTable(int pkts, double bytes) { - mobileRateEstimator.addSample(pkts, bytes); - } + void updateRate(int pkts, double bytes) { mobileRateEstimator.addSample(pkts, bytes); } - void resetMeasuresTable() { - mobileRateEstimator.resetMeasuresTable(); - } + void resetRate() { mobileRateEstimator.resetRate(); } - int getAverageValueFromTable(){ - return mobileRateEstimator.getRateKbps(); - } + int getAverageValue() { return mobileRateEstimator.getRate(); } }; /// Receiver-side statistics. @@ -202,7 +196,7 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - CMobileRateEstimator mobileRateEstimator; // The average Mbps over last second + CMovingRateEstimator mobileRateEstimator; // The average Mbps over last second void reset() { @@ -236,17 +230,11 @@ struct Receiver sentNak.resetTrace(); } - void fulfillMeasuresTable(int pkts, double bytes) { - mobileRateEstimator.addSample(pkts, bytes); - } + void updateRate(int pkts, double bytes) { mobileRateEstimator.addSample(pkts, bytes); } - void resetMeasuresTable() { - mobileRateEstimator.resetMeasuresTable(); - } + void resetRate() { mobileRateEstimator.resetRate(); } - int getAverageValueFromTable(){ - return mobileRateEstimator.getRateKbps(); - } + int getAverageValue() { return mobileRateEstimator.getRate(); } }; } // namespace stats From 768db18caf173dd6ee440fb596e872f873c4436e Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Thu, 22 Aug 2024 14:26:04 +0200 Subject: [PATCH 106/174] [core] Compiler warning fix --- srtcore/buffer_tools.cpp | 4 ++-- srtcore/buffer_tools.h | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 57e028f11..ff84cb695 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -157,9 +157,10 @@ void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes CSndRateEstimator::CSndRateEstimator(const time_point& tsNow) : m_tsFirstSampleTime(tsNow) - , m_iFirstSampleIdx(0) , m_iCurSampleIdx(0) , m_iRateBps(0) + , m_Samples(NUM_PERIODS) + , m_iFirstSampleIdx(0) { } @@ -275,7 +276,6 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const CMovingRateEstimator::CMovingRateEstimator() : CSndRateEstimator(sync::steady_clock::now()) - , m_Samples(NUM_PERIODS) { resetRate(0, NUM_PERIODS); } diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index dadafdd45..c75fd4b77 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -149,9 +149,9 @@ class CSndRateEstimator int getCurrentRate() const; protected: + time_point m_tsFirstSampleTime; //< Start time of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. int m_iRateBps; //< Rate in Bytes/sec. - time_point m_tsFirstSampleTime; //< Start time of the first sample. struct Sample { @@ -190,11 +190,12 @@ class CSndRateEstimator bool empty() const { return m_iPktsCount == 0; } }; + srt::FixedArray m_Samples; // Table of stored data + private: static const int NUM_PERIODS = 10; static const int SAMPLE_DURATION_MS = 100; // 100 ms - Sample m_Samples[NUM_PERIODS]; - int m_iFirstSampleIdx; //< Index of the first sample. + int m_iFirstSampleIdx; //< Index of the first sample. int incSampleIdx(int val, int inc = 1) const; }; @@ -218,10 +219,9 @@ class CMovingRateEstimator : CSndRateEstimator int getRate() const { return m_iRateBps; } private: - const int NUM_PERIODS = 100; // To get 1s of values - const int SAMPLE_DURATION_MS = 10; // 10 ms - time_point lastSlotTimestamp; // Used to compute the delta between 2 calls - srt::FixedArray m_Samples; // Table of stored data + const int NUM_PERIODS = 100; // To get 1s of values + const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point lastSlotTimestamp; // Used to compute the delta between 2 calls /// This method will compute the average value based on all table's measures and the period /// (NUM_PERIODS*SAMPLE_DURATION_MS) From 1d3e6083a5eeeb56742ed04bc7db9280628039db Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Thu, 22 Aug 2024 16:28:46 +0200 Subject: [PATCH 107/174] [core] Removing inheritance and fixing issue in rate compute --- srtcore/buffer_tools.cpp | 6 ++++- srtcore/buffer_tools.h | 47 +++++++++++++++++++++++++++++++++++++++- srtcore/core.cpp | 8 ++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index ff84cb695..194c49c09 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -275,7 +275,11 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const } CMovingRateEstimator::CMovingRateEstimator() - : CSndRateEstimator(sync::steady_clock::now()) + : m_tsFirstSampleTime(sync::steady_clock::now()) + , m_iFirstSampleIdx(0) + , m_iCurSampleIdx(0) + , m_iRateBps(0) + , m_Samples(NUM_PERIODS) { resetRate(0, NUM_PERIODS); } diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index c75fd4b77..0be738da6 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -200,7 +200,7 @@ class CSndRateEstimator int incSampleIdx(int val, int inc = 1) const; }; -class CMovingRateEstimator : CSndRateEstimator +class CMovingRateEstimator { typedef sync::steady_clock::time_point time_point; @@ -219,9 +219,54 @@ class CMovingRateEstimator : CSndRateEstimator int getRate() const { return m_iRateBps; } private: + // We would like responsiveness (accuracy) of rate estimation higher than 100 ms + // (ideally around 50 ms) for network adaptive algorithms. const int NUM_PERIODS = 100; // To get 1s of values const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point m_tsFirstSampleTime; //< Start time of the first sample. time_point lastSlotTimestamp; // Used to compute the delta between 2 calls + int m_iFirstSampleIdx; //< Index of the first sample. + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; //< Rate in Bytes/sec. + + struct Sample + { + int m_iPktsCount; // number of payload packets + int m_iBytesCount; // number of payload bytes + + void reset() + { + m_iPktsCount = 0; + m_iBytesCount = 0; + } + + Sample() + : m_iPktsCount(0) + , m_iBytesCount(0) + { + } + + Sample(int iPkts, int iBytes) + : m_iPktsCount(iPkts) + , m_iBytesCount(iBytes) + { + } + + Sample operator+(const Sample& other) + { + return Sample(m_iPktsCount + other.m_iPktsCount, m_iBytesCount + other.m_iBytesCount); + } + + Sample& operator+=(const Sample& other) + { + *this = *this + other; + return *this; + } + + bool empty() const { return m_iPktsCount == 0; } + }; + + srt::FixedArray m_Samples; // Table of stored data /// This method will compute the average value based on all table's measures and the period /// (NUM_PERIODS*SAMPLE_DURATION_MS) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 24d1879d5..f6d38e3a7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7571,13 +7571,13 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) // Average values management + // We are updating rate with 0 Byte 0 packet to ensure an up to date compute in case we are not sending packet for a while. + m_stats.sndr.updateRate(0, 0); + m_stats.rcvr.updateRate(0, 0); perf->mbpsSendRate = Bps2Mbps(m_stats.sndr.getAverageValue()); perf->mbpsRecvRate = Bps2Mbps(m_stats.rcvr.getAverageValue()); // 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 = m_iCongestionWindow; @@ -9730,6 +9730,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime m_stats.sndr.sent.count(payload); if (new_packet_packed) m_stats.sndr.sentUnique.count(payload); + m_stats.sndr.updateRate(1, payload); leaveCS(m_StatsLock); const duration sendint = m_tdSendInterval; @@ -10406,6 +10407,7 @@ int srt::CUDT::processData(CUnit* in_unit) enterCS(m_StatsLock); m_stats.rcvr.recvd.count(pktsz); + m_stats.rcvr.updateRate(1, pktsz); leaveCS(m_StatsLock); loss_seqs_t filter_loss_seqs; From a3c8a828e5731de0f8af0ce58227c08ea0362538 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Thu, 22 Aug 2024 17:50:34 +0200 Subject: [PATCH 108/174] [core] Changing stats var name --- srtcore/stats.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/srtcore/stats.h b/srtcore/stats.h index 16043fc35..4b67378fd 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -145,7 +145,7 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - CMovingRateEstimator mobileRateEstimator; // The average Mbps over last second + CMovingRateEstimator mavgRateEstimator; // The average Mbps over last second void reset() { @@ -171,11 +171,11 @@ struct Sender sentFilterExtra.resetTrace(); } - void updateRate(int pkts, double bytes) { mobileRateEstimator.addSample(pkts, bytes); } + void updateRate(int pkts, double bytes) { mavgRateEstimator.addSample(pkts, bytes); } - void resetRate() { mobileRateEstimator.resetRate(); } + void resetRate() { mavgRateEstimator.resetRate(); } - int getAverageValue() { return mobileRateEstimator.getRate(); } + int getAverageValue() { return mavgRateEstimator.getRate(); } }; /// Receiver-side statistics. @@ -196,7 +196,7 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - CMovingRateEstimator mobileRateEstimator; // The average Mbps over last second + CMovingRateEstimator mavgRateEstimator; // The average Mbps over last second void reset() { @@ -230,11 +230,11 @@ struct Receiver sentNak.resetTrace(); } - void updateRate(int pkts, double bytes) { mobileRateEstimator.addSample(pkts, bytes); } + void updateRate(int pkts, double bytes) { mavgRateEstimator.addSample(pkts, bytes); } - void resetRate() { mobileRateEstimator.resetRate(); } + void resetRate() { mavgRateEstimator.resetRate(); } - int getAverageValue() { return mobileRateEstimator.getRate(); } + int getAverageValue() { return mavgRateEstimator.getRate(); } }; } // namespace stats From 0eb1cf79bbb6613d9cb023b271b5e310537dd704 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Thu, 22 Aug 2024 17:59:30 +0200 Subject: [PATCH 109/174] [core] Restoring CSndRateEstimator --- srtcore/buffer_tools.cpp | 3 +-- srtcore/buffer_tools.h | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 194c49c09..bbe9da046 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -157,10 +157,9 @@ void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes CSndRateEstimator::CSndRateEstimator(const time_point& tsNow) : m_tsFirstSampleTime(tsNow) + , m_iFirstSampleIdx(0) , m_iCurSampleIdx(0) , m_iRateBps(0) - , m_Samples(NUM_PERIODS) - , m_iFirstSampleIdx(0) { } diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index 0be738da6..a4d552c29 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -148,11 +148,9 @@ class CSndRateEstimator /// including the current sampling interval. int getCurrentRate() const; -protected: - time_point m_tsFirstSampleTime; //< Start time of the first sample. - int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; //< Rate in Bytes/sec. - +private: + static const int NUM_PERIODS = 10; + static const int SAMPLE_DURATION_MS = 100; // 100 ms struct Sample { int m_iPktsCount; // number of payload packets @@ -190,14 +188,14 @@ class CSndRateEstimator bool empty() const { return m_iPktsCount == 0; } }; - srt::FixedArray m_Samples; // Table of stored data + int incSampleIdx(int val, int inc = 1) const; -private: - static const int NUM_PERIODS = 10; - static const int SAMPLE_DURATION_MS = 100; // 100 ms - int m_iFirstSampleIdx; //< Index of the first sample. + Sample m_Samples[NUM_PERIODS]; - int incSampleIdx(int val, int inc = 1) const; + time_point m_tsFirstSampleTime; //< Start time of the first sameple. + int m_iFirstSampleIdx; //< Index of the first sample. + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; // Input Rate in Bytes/sec }; class CMovingRateEstimator From 85161cff76efbcf120271cb202aa0aa2d62c46ac Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 26 Aug 2024 10:05:20 +0200 Subject: [PATCH 110/174] [core] Refactoring rate compute and variables name --- srtcore/buffer_tools.cpp | 18 ++++++++++-------- srtcore/buffer_tools.h | 14 +++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index bbe9da046..22a3d49cc 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -286,7 +286,7 @@ CMovingRateEstimator::CMovingRateEstimator() void CMovingRateEstimator::addSample(int pkts, double bytes) { const time_point now = steady_clock::now(); - const int iSampleDeltaIdx = (int)count_milliseconds(now - lastSlotTimestamp) / SAMPLE_DURATION_MS; + const int iSampleDeltaIdx = int(count_milliseconds(now - m_tsLastSlotTimestamp) / SAMPLE_DURATION_MS); if (iSampleDeltaIdx == 0) { @@ -308,10 +308,10 @@ void CMovingRateEstimator::addSample(int pkts, double bytes) m_Samples[m_iCurSampleIdx].m_iBytesCount = bytes; m_Samples[m_iCurSampleIdx].m_iPktsCount = pkts; - lastSlotTimestamp = now; - } + m_tsLastSlotTimestamp += milliseconds_from(SAMPLE_DURATION_MS * iSampleDeltaIdx); - computeAverageValue(); + computeAverageValue(); + } } void CMovingRateEstimator::resetRate(int from, int to) @@ -324,13 +324,15 @@ void CMovingRateEstimator::computeAverageValue() { const time_point now = steady_clock::now(); const int startDelta = count_milliseconds(now - m_tsFirstSampleTime); - const bool isFirstPeriod = startDelta < 1000; - m_iRateBps = 0; + const bool isFirstPeriod = startDelta < (SAMPLE_DURATION_MS * NUM_PERIODS); + int newRateBps = 0; for (int i = 0; i < NUM_PERIODS; i++) - m_iRateBps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)); + newRateBps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)); if (isFirstPeriod) - m_iRateBps = m_iRateBps * 1000 / startDelta; + newRateBps = newRateBps * 1000 / startDelta; + + m_iRateBps = newRateBps; } } // namespace srt diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index a4d552c29..e937df235 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -219,13 +219,13 @@ class CMovingRateEstimator private: // We would like responsiveness (accuracy) of rate estimation higher than 100 ms // (ideally around 50 ms) for network adaptive algorithms. - const int NUM_PERIODS = 100; // To get 1s of values - const int SAMPLE_DURATION_MS = 10; // 10 ms - time_point m_tsFirstSampleTime; //< Start time of the first sample. - time_point lastSlotTimestamp; // Used to compute the delta between 2 calls - int m_iFirstSampleIdx; //< Index of the first sample. - int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; //< Rate in Bytes/sec. + static const int NUM_PERIODS = 100; // To get 1s of values + static const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point m_tsFirstSampleTime; //< Start time of the first sample. + time_point m_tsLastSlotTimestamp; // Used to compute the delta between 2 calls + int m_iFirstSampleIdx; //< Index of the first sample. + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; //< Rate in Bytes/sec. struct Sample { From 1fb7fabba9a511aeb582c49ba57d3f31e3c9b7f0 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 26 Aug 2024 14:01:25 +0200 Subject: [PATCH 111/174] [core] Fixing division by 0. --- srtcore/buffer_tools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 22a3d49cc..4e9e83bb6 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -331,7 +331,7 @@ void CMovingRateEstimator::computeAverageValue() newRateBps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)); if (isFirstPeriod) - newRateBps = newRateBps * 1000 / startDelta; + newRateBps = newRateBps * SAMPLE_DURATION_MS * NUM_PERIODS / max(1, startDelta); m_iRateBps = newRateBps; } From a46819bfdd33a0a9ec08a74af2c17785363d4d79 Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 26 Aug 2024 14:23:38 +0200 Subject: [PATCH 112/174] [core] Removing unused variable (copy/paste from mother class) --- srtcore/buffer_tools.h | 1 - 1 file changed, 1 deletion(-) diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index e937df235..ebc5e6b79 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -223,7 +223,6 @@ class CMovingRateEstimator static const int SAMPLE_DURATION_MS = 10; // 10 ms time_point m_tsFirstSampleTime; //< Start time of the first sample. time_point m_tsLastSlotTimestamp; // Used to compute the delta between 2 calls - int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. int m_iRateBps; //< Rate in Bytes/sec. From cb44223370eb05e1e3d77cadedad6157c472851d Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux Date: Mon, 26 Aug 2024 14:25:15 +0200 Subject: [PATCH 113/174] [core] Removing unused variable from c file too... --- srtcore/buffer_tools.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 4e9e83bb6..cd6fcf21f 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -275,7 +275,6 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const CMovingRateEstimator::CMovingRateEstimator() : m_tsFirstSampleTime(sync::steady_clock::now()) - , m_iFirstSampleIdx(0) , m_iCurSampleIdx(0) , m_iRateBps(0) , m_Samples(NUM_PERIODS) From 50da65233820e851d3f72e9245be7e0cfb25ebf0 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Mon, 26 Aug 2024 14:36:07 +0200 Subject: [PATCH 114/174] Fixed unnecessary condition. Added more atomics (data race fix) --- srtcore/api.h | 4 ++++ srtcore/core.cpp | 12 +++--------- srtcore/core.h | 2 +- test/test_common.cpp | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index b5d6be915..48e7827f8 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -486,7 +486,11 @@ class CUDTUnited bool acquire(CUDTUnited& glob, CUDTSocket* s) { if (s == NULL) + { + socket = NULL; return false; + } + const bool caught = glob.acquireSocket(s); socket = caught ? s : NULL; return caught; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0e324e5fe..bfdbd94f9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3653,7 +3653,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // We can't record this address yet until the cookie-confirmation is done, for safety reasons. sockaddr_any use_source_adr(serv_addr.family()); - while (!m_bClosing) + while (!m_bClosing && !m_bBroken) { const steady_clock::time_point local_tnow = steady_clock::now(); const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load(); @@ -3815,12 +3815,6 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable. } - // The queue could have been kicked by the close() API call, - // if so, interrupt immediately. - if (m_bClosing || m_bBroken) - break; - - HLOGC(cnlog.Debug, log << CONID() << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); @@ -6868,7 +6862,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) << " DATA SIZE: " << size << " sched-SEQUENCE: " << seqno << " STAMP: " << BufferStamp(data, size)); - if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.time_since_epoch())) + if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.load().time_since_epoch())) { LOGC(aslog.Error, log << CONID() << "Wrong source time was provided. Sending is rejected."); @@ -11875,7 +11869,7 @@ int64_t srt::CUDT::socketStartTime(SRTSOCKET u) if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); - return count_microseconds(s->core().m_stats.tsStartTime.time_since_epoch()); + return count_microseconds(s->core().m_stats.tsStartTime.load().time_since_epoch()); } bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) diff --git a/srtcore/core.h b/srtcore/core.h index ed250c641..1c5b04008 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1184,7 +1184,7 @@ class CUDT private: // Trace struct CoreStats { - time_point tsStartTime; // timestamp when the UDT entity is started + atomic_time_point tsStartTime; // timestamp when the UDT entity is started stats::Sender sndr; // sender statistics stats::Receiver rcvr; // receiver statistics diff --git a/test/test_common.cpp b/test/test_common.cpp index 8d566b359..e705d3e82 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -129,7 +129,7 @@ TEST(SRTAPI, RapidClose) SRTSOCKET sock = srt_create_socket(); std::condition_variable cv_start; std::mutex cvm; - bool started = false, ended = false; + sync::atomic started(false), ended(false); std::thread connect_thread([&sock, &cv_start, &started, &ended] { started = true; From 0d78572e79a5d1f76d798d94f7f9376818430661 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Mon, 26 Aug 2024 14:52:06 +0200 Subject: [PATCH 115/174] Fixes for atomic field --- srtcore/core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index bfdbd94f9..7c0990954 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7499,7 +7499,7 @@ 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->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime.load()); perf->pktSent = m_stats.sndr.sent.trace.count(); perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); perf->pktRecv = m_stats.rcvr.recvd.trace.count(); @@ -9594,7 +9594,7 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) 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.usElapsed = count_microseconds(tnow - m_stats.tsStartTime.load()); g_snd_logger.state.usSRTT = m_iSRTT; g_snd_logger.state.usRTTVar = m_iRTTVar; g_snd_logger.state.msSndBuffSpan = buffdelay_ms; @@ -10983,7 +10983,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.load()) / 60000000) + distractor + correction; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; From fc9482b8a41e9b00be9c54b239032be79acaf399 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Mon, 2 Sep 2024 17:31:43 +0200 Subject: [PATCH 116/174] Really restored changes --- srtcore/api.cpp | 5 ++++ srtcore/logging.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++- srtcore/logging.h | 52 +++++++++++------------------------- 3 files changed, 84 insertions(+), 38 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index bb5dd64fe..bdfe4696d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4742,18 +4742,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; + srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); + srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); + srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4761,6 +4764,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); + srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4769,6 +4773,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); + srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d0ba3fd4a..d309b1b8a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -14,7 +14,7 @@ written by *****************************************************************************/ -#include "srt_compat.h" + #include "logging.h" using namespace std; @@ -23,6 +23,69 @@ using namespace std; namespace srt_logging { +// Note: subscribe() and unsubscribe() functions are being called +// in the global constructor and destructor only, as the +// Logger objects (and inside them also their LogDispatcher) +// are being created. It's not predicted that LogDispatcher +// object are going to be created any other way than as +// global objects. Therefore the construction and destruction +// of them happens always in the main thread. + +void LogConfig::subscribe(LogDispatcher* lg) +{ + vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); + if (p != loggers.end()) + return; // Do not register twice + + loggers.push_back(lg); +} + +void LogConfig::unsubscribe(LogDispatcher* lg) +{ + vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); + if (p != loggers.end()) + { + loggers.erase(p); + } +} + +// This function doesn't have any protection on itself, +// however the API functions from which it is called, call +// it already under a mutex protection. +void LogConfig::updateLoggersState() +{ + for (vector::iterator p = loggers.begin(); + p != loggers.end(); ++p) + { + (*p)->Update(); + } +} + +void LogDispatcher::Update() +{ + bool enabled_in_fa = src_config->enabled_fa[fa]; + enabled = enabled_in_fa && level <= src_config->max_level; +} + + +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + src_config->log_stream->flush(); + } + src_config->unlock(); +} + #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index 3f4efb286..37eb31b46 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,6 +20,7 @@ written by #include #include #include +#include #include #include #ifdef _WIN32 @@ -33,6 +34,7 @@ written by #include "utilities.h" #include "threadname.h" #include "logging_api.h" +#include "srt_compat.h" #include "sync.h" #ifdef __GNUC__ @@ -114,6 +116,7 @@ struct LogConfig void* loghandler_opaque; mutable srt::sync::Mutex mutex; int flags; + std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -136,6 +139,10 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() const { mutex.unlock(); } + + void subscribe(LogDispatcher*); + void unsubscribe(LogDispatcher*); + void updateLoggersState(); }; // The LogDispatcher class represents the object that is responsible for @@ -147,6 +154,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -157,6 +165,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), + enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -184,13 +193,18 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } + config.subscribe(this); + Update(); } ~LogDispatcher() { + src_config->unsubscribe(this); } - bool CheckEnabled(); + void Update(); + + bool CheckEnabled() { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -414,24 +428,6 @@ class Logger } }; -inline bool LogDispatcher::CheckEnabled() -{ - // Don't use enabler caching. Check enabled state every time. - - // These assume to be atomically read, so the lock is not needed - // (note that writing to this field is still mutex-protected). - // It's also no problem if the level was changed at the moment - // when the enabler check is tested here. Worst case, the log - // will be printed just a moment after it was turned off. - const LogConfig* config = src_config; // to enforce using const operator[] - config->lock(); - int configured_enabled_fa = config->enabled_fa[fa]; - int configured_maxlevel = config->max_level; - config->unlock(); - - return configured_enabled_fa && level <= configured_maxlevel; -} - #if HAVE_CXX11 @@ -482,24 +478,6 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - (*src_config->log_stream).flush(); - } - src_config->unlock(); -} - } #endif // INC_SRT_LOGGING_H From cb00ccead13e7d5f884ff229a6c637cadc0464db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 3 Sep 2024 17:57:10 +0200 Subject: [PATCH 117/174] Added option to disable test discovery (off by default) --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b135fc72..215559326 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,7 @@ option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) option(ENABLE_UNITTESTS "Enable unit tests" OFF) +option(DISABLE_UNITTESTS_DISCOVERY "Do not discover unit tests when enabled" OFF) option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off) option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off) @@ -1527,7 +1528,9 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - #gtest_discover_tests(test-srt) + if (NOT DISABLE_UNITTESTS_DISCOVERY) + gtest_discover_tests(test-srt) + endif() endif() enable_testing() From 5bc71e9506423760adb653f0b686eaf1d2452bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 4 Sep 2024 10:28:04 +0200 Subject: [PATCH 118/174] BUGFIX: the buffer info extraction function wasn't mutex-protected --- srtcore/core.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 802521d8a..b0ab9376c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8031,7 +8031,10 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) } if (m_config.bTSBPD || !m_config.bMessageAPI) { - // The getFirstNonreadSeqNo() function retuens the sequence number of the first packet + // NOTE: it's not only about protecting the buffer itself, it's also protecting + // the section where the m_iRcvCurrSeqNo is updated. + ScopedLock buflock (m_RcvBufferLock); + // The getFirstNonreadSeqNo() function returns the sequence number of the first packet // that cannot be read. In cases when a message can consist of several data packets, // an existing packet of partially available message also cannot be read. // If TSBPD mode is enabled, a message must consist of a single data packet only. From 90c28886b93f01a21e7b514277053cc9e6ec4b30 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 10 Sep 2024 14:41:33 +0200 Subject: [PATCH 119/174] Updated. Added new test from #1891. Added doc info --- docs/API/API-socket-options.md | 5 ++ test/test_bonding.cpp | 131 ++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index a06f8556d..438b90637 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -505,6 +505,11 @@ allowed must take this into consideration. It's up to the caller of this function to make this distinction and to take appropriate action depending on the type of entity returned. +Note: this flag should be altered **before** calling `srt_listen`. If you do +this after this call, you might have some pending group connections in the +meantime that will be rejected because group connections are not **yet** +allowed on this listener socket. + When this flag is set to 1 on an accepted socket that is passed to the listener callback handler, it means that this socket is created for a group connection and it will become a member of a group. Note that in this case diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 0e48c8a04..eded4c20b 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -6,8 +6,11 @@ #include "gtest/gtest.h" #include "test_env.h" +#include "apputil.hpp" // Note: declares CreateAddr, but not srt::CreateAddr #include "srt.h" +#include "logging_api.h" +#include "common.h" #include "netinet_any.h" TEST(Bonding, SRTConnectGroup) @@ -376,7 +379,7 @@ TEST(Bonding, Options) #endif int allow = 1; ASSERT_NE(srt_setsockflag(lsn, SRTO_GROUPCONNECT, &allow, sizeof allow), SRT_ERROR); - sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + sockaddr_any sa = srt::CreateAddr("127.0.0.1", 5555, AF_INET); ASSERT_NE(srt_bind(lsn, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(lsn, 1), SRT_ERROR); started = true; @@ -413,7 +416,7 @@ TEST(Bonding, Options) } // Now the thread is accepting, so we call the connect. - sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + sockaddr_any sa = srt::CreateAddr("127.0.0.1", 5555, AF_INET); SRTSOCKET member = srt_connect(grp, sa.get(), sa.size()); // We've released the mutex and signaled the CV, so accept should proceed now. @@ -507,7 +510,7 @@ TEST(Bonding, InitialFailure) int allow = 1; ASSERT_NE(srt_setsockflag(lsn, SRTO_GROUPCONNECT, &allow, sizeof allow), SRT_ERROR); - sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + sockaddr_any sa = srt::CreateAddr("127.0.0.1", 5555, AF_INET); ASSERT_NE(srt_bind(lsn, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(lsn, 5), SRT_ERROR); @@ -564,3 +567,125 @@ TEST(Bonding, InitialFailure) srt_close(lsn); } +void SetLongSilenceTolerant(const SRTSOCKET s) +{ + int longtime = 100000; + + srt_setsockflag(s, SRTO_CONNTIMEO, &longtime, sizeof longtime); + srt_setsockflag(s, SRTO_PEERIDLETIMEO, &longtime, sizeof longtime); +} + +TEST(Bonding, DeadLinkUpdate) +{ + using namespace std; + using namespace std::chrono; + + srt::TestInit srtinit; + + SRTSOCKET listener = srt_create_socket(); + const SRTSOCKET group = srt_create_group(SRT_GTYPE_BACKUP); + + SetLongSilenceTolerant(listener); + SetLongSilenceTolerant(group); + + srt::sockaddr_any sa(AF_INET); + + inet_pton(AF_INET, "127.0.0.1", sa.get_addr()); + + sa.hport(5555); + + srt_bind(listener, sa.get(), sa.size()); + srt::setopt(listener)[SRTO_GROUPCONNECT] = 1; + srt_listen(listener, 1); + char srcbuf [] = "1234ABCD"; + + thread td = thread([&]() { + cout << "[T] Connecting 1...\n"; + const SRTSOCKET member1 = srt_connect(group, sa.get(), sa.size()); + EXPECT_NE(member1, SRT_INVALID_SOCK); + // Now wait 3s + cout << "[T] Link 1 established. Wait 3s...\n"; + this_thread::sleep_for(seconds(3)); + + cout << "[T] Connecting 2...\n"; + // Make a second connection + const SRTSOCKET member2 = srt_connect(group, sa.get(), sa.size()); + EXPECT_NE(member2, SRT_INVALID_SOCK); + + if (member2 == SRT_INVALID_SOCK || member1 == SRT_INVALID_SOCK) + { + srt_close(member1); + srt_close(member2); + cout << "[T] Test already failed, exitting\n"; + return; + } + + cout << "[T] Link 2 established. Wait 3s...\n"; + // Again wait 3s + this_thread::sleep_for(seconds(3)); + + cout << "[T] Killing link 1...\n"; + // Now close the first connection + srt_close(member1); + + // Now send the data and see if they are received + cout << "[T] Sending: size=" << (sizeof srcbuf) << " Content: '" << srcbuf << "'...\n"; + int nsent = srt_send(group, srcbuf, sizeof srcbuf); + EXPECT_NE(nsent, -1) << "srt_send:" << srt_getlasterror_str(); + + cout << "[T] Wait 3s...\n"; + // Again wait 3s + this_thread::sleep_for(seconds(3)); + + cout << "[T] Killing the group and exitting.\n"; + // And close + srt_close(group); + + cout << "[T] exit\n"; + }); + + cout << "Accepting (10s timeout)...\n"; + + SRTSOCKET lsnra [] = { listener }; + + // Using srt_accept_bond to apply accept timeout + const SRTSOCKET acp = srt_accept_bond(lsnra, 1, 10*1000); + + EXPECT_NE(acp, -1) << "srt_accept:" << srt_getlasterror_str(); + EXPECT_EQ(acp & SRTGROUP_MASK, SRTGROUP_MASK); + + // Close and set up the listener again. + srt_close(listener); + if (acp != SRT_ERROR) + { + listener = srt_create_socket(); + srt_bind(listener, sa.get(), sa.size()); + srt::setopt(listener)[SRTO_GROUPCONNECT] = 1; + srt_listen(listener, 1); + + cout << "Group accepted. Receiving...\n"; + char buf[1316] = ""; + const int nrecv = srt_recv(acp, buf, 1316); + int syserr, err; + err = srt_getlasterror(&syserr); + EXPECT_NE(nrecv, -1) << "srt_recv:" << srt_getlasterror_str(); + + cout << "Received: val=" << nrecv << " Content: '" << buf << "'\n"; + if (nrecv == -1) + { + cout << "ERROR: " << srt_strerror(err, syserr) << endl; + cout << "STATUS: " << srt_logging::SockStatusStr(srt_getsockstate(acp)) << endl; + } + else + { + EXPECT_EQ(strcmp(srcbuf, buf), 0); + } + + cout << "Closing.\n"; + srt_close(acp); + srt_close(listener); + } + + td.join(); +} + From a5161409b6399f06e2b639caf9e03b1f8f77f13f Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 10 Sep 2024 15:49:14 +0200 Subject: [PATCH 120/174] Fixed right types for EXPECT_EQ calls in new tests --- test/test_epoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 2c8d58925..335a0b3ef 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -656,7 +656,7 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // SUBSCRIBED EVENTS: IN, UPDATE. // expected: UPDATE only. - EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_EQ(SRT_EPOLL_OPT(fdset[0].events), SRT_EPOLL_UPDATE); SRTSOCKET joined = extra_call.get(); EXPECT_NE(joined, SRT_INVALID_SOCK); std::cout << Sprint("Extra joined: @", joined, "\n"); @@ -915,7 +915,7 @@ void testMultipleListenerReady(const bool LATE_CALL) // SUBSCRIBED EVENTS: IN, UPDATE. // expected: UPDATE only. - EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_EQ(SRT_EPOLL_OPT(fdset[0].events), SRT_EPOLL_UPDATE); EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); std::cout << "Joining connector thread(s)\n"; From 461def598584d779ef9ee044c7ba7631d4da7109 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 11 Sep 2024 08:52:57 +0200 Subject: [PATCH 121/174] Added setting UPDATE event on group on new connection. Fixed test with accept timeout --- srtcore/api.cpp | 9 +++++++++ test/test_bonding.cpp | 5 +---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 0cef76383..0c6df0ac0 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -805,6 +805,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // 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->core().m_sPollID, SRT_EPOLL_UPDATE, true); +#if ENABLE_BONDING + // Note that the code in this current IF branch can only be executed in case + // of group members. Otherwise should_submit_to_accept will be always true. + if (ns->m_GroupOf) + { + HLOGC(gmlog.Debug, log << "GROUP UPDATE $" << ns->m_GroupOf->id() << " per connected socket @" << ns->m_SocketID); + m_EPoll.update_events(ns->m_GroupOf->id(), ns->m_GroupOf->m_sPollID, SRT_EPOLL_UPDATE, true); + } +#endif CGlobEvent::triggerEvent(); } diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index eded4c20b..1d38e3a41 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -640,15 +640,12 @@ TEST(Bonding, DeadLinkUpdate) cout << "[T] Killing the group and exitting.\n"; // And close srt_close(group); - cout << "[T] exit\n"; }); cout << "Accepting (10s timeout)...\n"; - - SRTSOCKET lsnra [] = { listener }; - // Using srt_accept_bond to apply accept timeout + SRTSOCKET lsnra [] = { listener }; const SRTSOCKET acp = srt_accept_bond(lsnra, 1, 10*1000); EXPECT_NE(acp, -1) << "srt_accept:" << srt_getlasterror_str(); From 51b2555a765b8f05c968b2e4485b7e15ad83a473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 24 Sep 2024 18:59:42 +0200 Subject: [PATCH 122/174] Fixed build break in log --- srtcore/core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c33c8e4ab..95f7ee47a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5952,8 +5952,7 @@ bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const socka // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, - log << CONID() - log << "createSendHSResponse: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + log << CONID() << "createSendHSResponse: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "createSendHSResponse: error creating handshake response"); From 739adf0885cbe68ff336510ec487d450beea036a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 25 Sep 2024 10:33:39 +0200 Subject: [PATCH 123/174] Changed blocked test code to conditionally-enabled --- srtcore/core.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 95f7ee47a..520c7f5ec 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -50,6 +50,9 @@ modified by Haivision Systems Inc. *****************************************************************************/ +// Set this to 5 to fake "lost" handshake packets 5 times in a row +#define SRT_ENABLE_FAKE_LOSS_HS 0 + #include "platform_sys.h" // Linux specific @@ -4754,15 +4757,15 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. - /// TESTING - unblock if necessary. - /// This part fictionally "loses" incoming conclusion HS 5 times. - /* - static int fail_count = 5; + // This part fictionally "loses" incoming conclusion HS given number of times. +#if SRT_ENABLE_FAKE_LOSS_HS > 0 + static int fail_count = SRT_ENABLE_FAKE_LOSS_HS; if (--fail_count) { LOGC(cnlog.Note, log << "postConnect: FAKE LOSS HS conclusion message"); return CONN_CONTINUE; - } // */ + } +#endif // This procedure isn't being executed in rendezvous because // in rendezvous it's completed before calling this function. From 1e57db329c10e30cb90bfe050a32fe179627ec42 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 18 Dec 2024 13:42:14 +0100 Subject: [PATCH 124/174] [BUG] Fixed desynch between deleted socket and unsubscribing from epoll (misleading log flood) --- srtcore/api.cpp | 18 +++++++++++++++++- srtcore/core.cpp | 37 +------------------------------------ srtcore/epoll.cpp | 27 +++++++++++++++++++++++++++ srtcore/epoll.h | 2 ++ test/test_bonding.cpp | 22 ++++++++++++++++++---- 5 files changed, 65 insertions(+), 41 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index bb5dd64fe..45cfaca7f 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -833,6 +833,9 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, ns->removeFromGroup(true); } #endif + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(id, ns->core().m_sPollID); + m_Sockets.erase(id); m_ClosedSockets[id] = ns; } @@ -1557,6 +1560,9 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i targets[tii].errorcode = e.getErrorCode(); targets[tii].id = CUDT::INVALID_SOCK; + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(ns->m_SocketID, ns->core().m_sPollID); + ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); m_Sockets.erase(ns->m_SocketID); @@ -1571,6 +1577,8 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i targets[tii].id = CUDT::INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(ns->m_SocketID, ns->core().m_sPollID); m_Sockets.erase(ns->m_SocketID); // Intercept to delete the socket on failure. delete ns; @@ -2063,6 +2071,9 @@ int srt::CUDTUnited::close(CUDTSocket* s) } #endif + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(s->m_SocketID, s->core().m_sPollID); + m_Sockets.erase(s->m_SocketID); m_ClosedSockets[s->m_SocketID] = s; HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); @@ -2851,6 +2862,10 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; as->breakSocket_LOCKED(); + + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(as->m_SocketID, as->core().m_sPollID); + m_ClosedSockets[q->first] = as; m_Sockets.erase(q->first); } @@ -2870,7 +2885,8 @@ 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->core().m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, false); + // (just in case, this should be wiped out already) + m_EPoll.wipe_usock(u, s->core().m_sPollID); // delete this one m_ClosedSockets.erase(i); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eca2b2069..4ef1454bc 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6282,42 +6282,7 @@ bool srt::CUDT::closeInternal() ATR_NOEXCEPT * it would remove the socket from the EPoll after close. */ - // Make a copy under a lock because other thread might access it - // at the same time. - enterCS(uglobal().m_EPoll.m_EPollLock); - set epollid = m_sPollID; - leaveCS(uglobal().m_EPoll.m_EPollLock); - - // trigger any pending IO events. - HLOGC(smlog.Debug, log << CONID() << "close: SETTING ERR readiness on E" << Printable(epollid)); - 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) - { - HLOGC(smlog.Debug, log << CONID() << "close: CLEARING subscription on E" << (*i)); - try - { - uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events); - } - catch (...) - { - // The goal of this loop is to remove all subscriptions in - // the epoll system to this socket. If it's unsubscribed already, - // that's even better. - } - HLOGC(smlog.Debug, log << CONID() << "close: removing E" << (*i) << " from back-subscribers"); - } - - // Not deleting elements from m_sPollID inside the loop because it invalidates - // the control iterator of the loop. Instead, all will be removed at once. - - // 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(uglobal().m_EPoll.m_EPollLock); - m_sPollID.clear(); - leaveCS(uglobal().m_EPoll.m_EPollLock); + uglobal().m_EPoll.wipe_usock(m_SocketID, m_sPollID); // XXX What's this, could any of the above actions make it !m_bOpened? if (!m_bOpened) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 8cd8440c7..bc52ec10b 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -945,6 +945,33 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return nupdated; } +/// This is a simple function which removes the socket from epoll system. +/// The subscription list should be provided in the @a eids container and +/// the socket is removed from each of them, then this is cleared. This +/// should be the socket's private EID container that keeps EIDs that it +/// should update when an appropriate event comes. +/// +/// @param uid Socket ID that has to be removed from the epoll system +/// @param eids EIDs that the given socket believes being subscribed in +void srt::CEPoll::wipe_usock(const SRTSOCKET uid, std::set& eids) +{ + ScopedLock pg (m_EPollLock); + for (set::iterator i = eids.begin(); i != eids.end(); ++ i) + { + map::iterator p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + HLOGC(eilog.Note, log << "epoll/wipe: E" << *i << " was deleted in the meantime"); + continue; + } + + CEPollDesc& ed = p->second; + ed.removeSubscription(uid); + } + + eids.clear(); +} + // Debug use only. #if ENABLE_HEAVY_LOGGING namespace srt diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 00d46ceb4..3ac190186 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -489,6 +489,8 @@ friend class srt::CRendezvousQueue; int update_events(const SRTSOCKET& uid, std::set& eids, int events, bool enable); + void wipe_usock(const SRTSOCKET uid, std::set& eids); + int setflags(const int eid, int32_t flags); private: diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 4ec4a84f5..207f44065 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "gtest/gtest.h" @@ -62,6 +63,8 @@ TEST(Bonding, SRTConnectGroup) #define EXPECT_SRT_SUCCESS(callform) EXPECT_NE(callform, -1) << "SRT ERROR: " << srt_getlasterror_str() +static std::mutex g_listening_stopped; + void listening_thread(bool should_read) { const SRTSOCKET server_sock = srt_create_socket(); @@ -118,6 +121,9 @@ void listening_thread(bool should_read) } } + std::cout << "Listen: wait for green light from the caller...\n"; + std::unique_lock listen_lock (g_listening_stopped); + srt_close(acp); srt_close(server_sock); @@ -135,8 +141,8 @@ int g_nfailed = 0; void ConnectCallback(void* , SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token) { std::cout << "Connect callback. Socket: " << sock - << ", error: " << error - << ", token: " << token << '\n'; + << ", error: " << error << " (" << srt_strerror(error, 0) + << "), token: " << token << '\n'; if (error == SRT_SUCCESS) ++g_nconnected; @@ -171,6 +177,10 @@ TEST(Bonding, NonBlockingGroupConnect) sockaddr_in safail = sa; safail.sin_port = htons(4201); // port where we have no listener + // We need to keep the listener with the socket without closing it + // until we are done. + std::unique_lock listen_lock (g_listening_stopped); + std::future listen_promise = std::async(std::launch::async, std::bind(&listening_thread, false)); std::cout << "Connecting two sockets " << std::endl; @@ -203,7 +213,7 @@ TEST(Bonding, NonBlockingGroupConnect) write, &wlen, 5000, /* timeout */ 0, 0, 0, 0); - + std::cout << "Epoll result: " << epoll_res << '\n'; std::cout << "Epoll rlen: " << rlen << ", wlen: " << wlen << '\n'; for (int i = 0; i < rlen; ++i) @@ -212,10 +222,14 @@ TEST(Bonding, NonBlockingGroupConnect) } for (int i = 0; i < wlen; ++i) { - std::cout << "Epoll write[" << i << "]: " << write[i] << " (removed from epoll)\n"; + SRT_SOCKSTATUS st = srt_getsockstate(write[i]); + std::cout << "Epoll write[" << i << "]: " << write[i] + << " ST:" << srt_logging::SockStatusStr(st) + << " (removing from epoll)\n"; EXPECT_EQ(srt_epoll_remove_usock(poll_id, write[i]), 0); } } + listen_lock.unlock(); // give green light to the listener so that it closes sockets. listen_promise.wait(); From 5cba144764c5e6caf520d7c403368b4f794c5745 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 19 Dec 2024 11:03:48 +0100 Subject: [PATCH 125/174] Reworked attributes. Put inc() method of CSeqNo in use for clarity --- srtcore/buffer_rcv.cpp | 4 +-- srtcore/common.h | 10 +++---- srtcore/srt.h | 4 +++ srtcore/srt_attr_defs.h | 61 +++++++++++++++++++++-------------------- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 5e764aeda..f6afc8be9 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -919,7 +919,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iEndOff = decOff(m_iEndOff, 1); m_iDropOff = decOff(m_iDropOff, 1); - ++m_iStartSeqNo; + m_iStartSeqNo = m_iStartSeqNo.inc(); } else m_iNotch += rs; @@ -1176,7 +1176,7 @@ int CRcvBuffer::releaseNextFillerEntries() break; } - ++m_iStartSeqNo; + m_iStartSeqNo = m_iStartSeqNo.inc(); releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; diff --git a/srtcore/common.h b/srtcore/common.h index 7174f0c87..39774b00b 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -685,19 +685,19 @@ class CSeqNo inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} - CSeqNo inc() const { return CSeqNo(incseq(value)); } + SRT_ATR_NODISCARD CSeqNo inc() const { return CSeqNo(incseq(value)); } inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} - CSeqNo dec() const { return CSeqNo(decseq(value)); } + SRT_ATR_NODISCARD CSeqNo dec() const { return CSeqNo(decseq(value)); } inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) - CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } + SRT_ATR_NODISCARD CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } inline static int32_t decseq(int32_t seq, int32_t dec) { @@ -705,13 +705,13 @@ class CSeqNo if ( seq < dec ) { int32_t left = dec - seq; // This is so many that is left after dragging dec to 0 - // So now decrement the (m_iMaxSeqNo+1) by "left" + // So now decrement the (m_iMaxSeqNo+1) by "left" return m_iMaxSeqNo - left + 1; } return seq - dec; } - CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } + SRT_ATR_NODISCARD CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } static int32_t maxseq(int32_t seq1, int32_t seq2) { diff --git a/srtcore/srt.h b/srtcore/srt.h index 614a85aea..afb969458 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -109,17 +109,21 @@ written by #define SRT_ATR_DEPRECATED #define SRT_ATR_DEPRECATED_PX [[deprecated]] +#define SRT_ATR_NODISCARD [[nodiscard]] // GNUG is GNU C/C++; this syntax is also supported by Clang #elif defined(__GNUC__) #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED __attribute__((deprecated)) +#define SRT_ATR_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) #define SRT_ATR_DEPRECATED_PX __declspec(deprecated) #define SRT_ATR_DEPRECATED // no postfix-type modifier +#define SRT_ATR_NODISCARD #else #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED +#define SRT_ATR_NODISCARD #endif #ifdef __cplusplus diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 726c4a03b..2c71d80d5 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -17,26 +17,17 @@ used by SRT library internally. // 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 HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#ifdef __GNUG__ +#define HAVE_GCC 1 #else -#define SRT_ATR_ALIGNAS(n) +#define HAVE_GCC 0 #endif #if defined(__cplusplus) && __cplusplus > 199711L @@ -44,18 +35,15 @@ used by SRT library internally. // 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 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + #endif #elif defined(_MSC_VER) && _MSC_VER >= 1800 // Microsoft Visual Studio supports C++11, but not fully, @@ -65,26 +53,41 @@ used by SRT library internally. #define HAVE_CXX11 1 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 #define HAVE_FULL_CXX11 1 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + +#endif +#else +#define HAVE_CXX11 0 +#define HAVE_CXX17 0 +#endif // __cplusplus + +#if HAVE_FULL_CXX11 #define ATR_NOEXCEPT noexcept #define ATR_NOTHROW noexcept #define ATR_CONSTEXPR constexpr #define ATR_OVERRIDE override #define ATR_FINAL final #else +// These are both for HAVE_CXX11 == 1 and 0. #define ATR_NOEXCEPT #define ATR_NOTHROW throw() #define ATR_CONSTEXPR #define ATR_OVERRIDE #define ATR_FINAL #endif + +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) #else -#define HAVE_CXX11 0 -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#endif // __cplusplus +#define SRT_ATR_ALIGNAS(n) +#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." From 89d008a54826e5cc9f40b6da9fbd097b9371c1c3 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 9 Jan 2025 14:11:17 +0100 Subject: [PATCH 126/174] [API] Set up length limits for STREAMID and PACKETFILTER in the API --- srtcore/socketconfig.h | 5 +++-- srtcore/srt.h | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index a6dd06caa..a3c2c0c02 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -56,6 +56,7 @@ written by #include #endif #include +#include "srt.h" #include "haicrypt.h" #include "congctl.h" #include "packet.h" @@ -212,8 +213,8 @@ struct CSrtConfig: CSrtMuxerConfig // Mimimum recv flight flag size is 32 packets 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_SID_LENGTH = SRT_STREAMID_MAX; + static const size_t MAX_PFILTER_LENGTH = SRT_PACKETFILTER_MAX; static const size_t MAX_CONG_LENGTH = 16; int iMSS; // Maximum Segment Size, in bytes diff --git a/srtcore/srt.h b/srtcore/srt.h index 614a85aea..fbe34c62d 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -295,6 +295,12 @@ static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hd // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; +// Maximum number of characters for SRTO_STREAMID +static const size_t SRT_STREAMID_MAX = 512; + +// Maximum number of characters for packet filter configuration, SRTO_PACKETFILTER +static const size_t SRT_PACKETFILTER_MAX = 64; + // Importrant note: please add new fields to this structure to the end and don't remove any existing fields struct CBytePerfMon { From 9c110195c0b8b52b3e02fed1421dc3e131c7ba6b Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 4 Mar 2025 13:21:32 +0100 Subject: [PATCH 127/174] Cosmetics. Added MSVC version of [[nodiscard]]. --- srtcore/buffer_rcv.cpp | 2 +- srtcore/srt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index f6afc8be9..f8e9c7aca 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -814,7 +814,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair Date: Tue, 4 Mar 2025 13:28:45 +0100 Subject: [PATCH 128/174] [MAINT] Added optional test discovery. Added improvements for attributes --- CMakeLists.txt | 5 +++- srtcore/srt.h | 4 +++ srtcore/srt_attr_defs.h | 61 +++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d718792d1..bb1643044 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,7 @@ option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) option(ENABLE_UNITTESTS "Enable unit tests" OFF) +option(ENABLE_UNITTESTS_DISCOVERY "Do not discover unit tests when enabled" ON) option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off) option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off) @@ -1566,7 +1567,9 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - gtest_discover_tests(test-srt) + if (ENABLE_UNITTESTS_DISCOVERY) + gtest_discover_tests(test-srt) + endif() endif() enable_testing() diff --git a/srtcore/srt.h b/srtcore/srt.h index 71ac2c3af..2d8ba3997 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -109,17 +109,21 @@ written by #define SRT_ATR_DEPRECATED #define SRT_ATR_DEPRECATED_PX [[deprecated]] +#define SRT_ATR_NODISCARD [[nodiscard]] // GNUG is GNU C/C++; this syntax is also supported by Clang #elif defined(__GNUC__) #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED __attribute__((deprecated)) +#define SRT_ATR_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) #define SRT_ATR_DEPRECATED_PX __declspec(deprecated) #define SRT_ATR_DEPRECATED // no postfix-type modifier +#define SRT_ATR_NODISCARD _Check_return_ #else #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED +#define SRT_ATR_NODISCARD #endif #ifdef __cplusplus diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 726c4a03b..2c71d80d5 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -17,26 +17,17 @@ used by SRT library internally. // 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 HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#ifdef __GNUG__ +#define HAVE_GCC 1 #else -#define SRT_ATR_ALIGNAS(n) +#define HAVE_GCC 0 #endif #if defined(__cplusplus) && __cplusplus > 199711L @@ -44,18 +35,15 @@ used by SRT library internally. // 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 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + #endif #elif defined(_MSC_VER) && _MSC_VER >= 1800 // Microsoft Visual Studio supports C++11, but not fully, @@ -65,26 +53,41 @@ used by SRT library internally. #define HAVE_CXX11 1 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 #define HAVE_FULL_CXX11 1 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + +#endif +#else +#define HAVE_CXX11 0 +#define HAVE_CXX17 0 +#endif // __cplusplus + +#if HAVE_FULL_CXX11 #define ATR_NOEXCEPT noexcept #define ATR_NOTHROW noexcept #define ATR_CONSTEXPR constexpr #define ATR_OVERRIDE override #define ATR_FINAL final #else +// These are both for HAVE_CXX11 == 1 and 0. #define ATR_NOEXCEPT #define ATR_NOTHROW throw() #define ATR_CONSTEXPR #define ATR_OVERRIDE #define ATR_FINAL #endif + +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) #else -#define HAVE_CXX11 0 -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#endif // __cplusplus +#define SRT_ATR_ALIGNAS(n) +#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." From da0d2df61d1f755b49457c47e826682b6e3e754c Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 12 Mar 2025 16:13:18 +0100 Subject: [PATCH 129/174] Minimized changes. Fixed incorrect test initialization --- srtcore/api.cpp | 71 +++++++++++++++++---------------- srtcore/core.cpp | 15 ++++--- test/test_common.cpp | 3 +- test/test_file_transmission.cpp | 2 - 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ccae447ab..e6b53aa9d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2052,41 +2052,42 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); - // The check for whether m_pRcvQueue isn't NULL is safe enough; - // it can either be NULL after socket creation and without binding - // and then once it's assigned, it's never reset to NULL even when - // destroying the socket. - CUDT& e = s->core(); - if (e.m_pRcvQueue && e.m_bConnecting && !e.m_bConnected) - { - // Workaround for a design flaw. - // It's to work around the case when the socket is being - // closed in another thread while it's in the process of - // connecting in the blocking mode, that is, it runs the - // loop in `CUDT::startConnect` whole time under the lock - // of CUDT::m_ConnectionLock and CUDTSocket::m_ControlLock - // this way blocking the `srt_close` API call from continuing. - // We are setting here the m_bClosing flag prematurely so - // that the loop may check this flag periodically and exit - // immediately if it's set. - // - // The problem is that this flag shall NOT be set in case - // when you have a CONNECTED socket because not only isn't it - // not a problem in this case, but also it additionally - // turns the socket in a "confused" state in which it skips - // vital part of closing itself and therefore runs an infinite - // loop when trying to purge the sender buffer of the closing - // socket. - // - // XXX Consider refax on CUDT::startConnect and removing the - // connecting loop there and replace the "blocking mode specific" - // connecting procedure with delegation to the receiver queue, - // which will be then common with non-blocking mode, and synchronize - // the blocking through a CV. - - e.m_bClosing = true; - e.m_pRcvQueue->kick(); - } + + // The check for whether m_pRcvQueue isn't NULL is safe enough; + // it can either be NULL after socket creation and without binding + // and then once it's assigned, it's never reset to NULL even when + // destroying the socket. + CUDT& e = s->core(); + if (e.m_pRcvQueue && e.m_bConnecting && !e.m_bConnected) + { + // Workaround for a design flaw. + // It's to work around the case when the socket is being + // closed in another thread while it's in the process of + // connecting in the blocking mode, that is, it runs the + // loop in `CUDT::startConnect` whole time under the lock + // of CUDT::m_ConnectionLock and CUDTSocket::m_ControlLock + // this way blocking the `srt_close` API call from continuing. + // We are setting here the m_bClosing flag prematurely so + // that the loop may check this flag periodically and exit + // immediately if it's set. + // + // The problem is that this flag shall NOT be set in case + // when you have a CONNECTED socket because not only isn't it + // not a problem in this case, but also it additionally + // turns the socket in a "confused" state in which it skips + // vital part of closing itself and therefore runs an infinite + // loop when trying to purge the sender buffer of the closing + // socket. + // + // XXX Consider refax on CUDT::startConnect and removing the + // connecting loop there and replace the "blocking mode specific" + // connecting procedure with delegation to the receiver queue, + // which will be then common with non-blocking mode, and synchronize + // the blocking through a CV. + + e.m_bClosing = true; + e.m_pRcvQueue->kick(); + } ScopedLock socket_cg(s->m_ControlLock); HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing from listening, closing CUDT)"); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 8755a971b..9e44b9fc4 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3681,7 +3681,8 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // We can't record this address yet until the cookie-confirmation is done, for safety reasons. sockaddr_any use_source_adr(serv_addr.family()); - while (!m_bClosing && !m_bBroken) + while (!m_bClosing) + //while (!m_bClosing && !m_bBroken) { const steady_clock::time_point local_tnow = steady_clock::now(); const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load(); @@ -6902,7 +6903,8 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) << " DATA SIZE: " << size << " sched-SEQUENCE: " << seqno << " STAMP: " << BufferStamp(data, size)); - if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.load().time_since_epoch())) + time_point start_time = m_stats.tsStartTime; + if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(start_time.time_since_epoch())) { LOGC(aslog.Error, log << CONID() << "Wrong source time was provided. Sending is rejected."); @@ -7539,7 +7541,7 @@ 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.load()); + perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); perf->pktSent = m_stats.sndr.sent.trace.count(); perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); perf->pktRecv = m_stats.rcvr.recvd.trace.count(); @@ -9647,7 +9649,7 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) 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.load()); + 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; @@ -11036,7 +11038,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.load()) / 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; @@ -11923,7 +11925,8 @@ int64_t srt::CUDT::socketStartTime(SRTSOCKET u) if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); - return count_microseconds(s->core().m_stats.tsStartTime.load().time_since_epoch()); + const time_point& start_time = s->core().m_stats.tsStartTime; + return count_microseconds(start_time.time_since_epoch()); } bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) diff --git a/test/test_common.cpp b/test/test_common.cpp index e705d3e82..df85cefd6 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -78,7 +78,6 @@ TEST(CIPAddress, IPv4_in_IPv6_pton) test_cipaddress_pton(peer_ip, AF_INET6, ip); } - TEST(SRTAPI, SyncRendezvousHangs) { srt::TestInit srtinit; @@ -124,7 +123,7 @@ TEST(SRTAPI, SyncRendezvousHangs) TEST(SRTAPI, RapidClose) { - ASSERT_EQ(srt_startup(), 0); + srt::TestInit srtinit; SRTSOCKET sock = srt_create_socket(); std::condition_variable cv_start; diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 61d3d6a20..bfd668ac7 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -18,7 +18,6 @@ #endif #include "srt.h" -#include "threadname.h" #include #include @@ -100,7 +99,6 @@ TEST(Transmission, FileUpload) auto client = std::thread([&] { - srt::ThreadName::set("TEST-in"); sockaddr_in remote; int len = sizeof remote; const SRTSOCKET accepted_sock = srt_accept(sock_lsn, (sockaddr*)&remote, &len); From c8800a4e325cec2cd120ebd45efe44a18427f6b9 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 12 Mar 2025 16:23:25 +0100 Subject: [PATCH 130/174] Fixed pedantic C++ --- srtcore/core.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9e44b9fc4..9bd83d002 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7539,9 +7539,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) ScopedLock statsguard(m_StatsLock); - const steady_clock::time_point currtime = steady_clock::now(); + const time_point currtime = steady_clock::now(), + start_time = m_stats.tsStartTime; - perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); + perf->msTimeStamp = count_milliseconds(currtime - start_time); perf->pktSent = m_stats.sndr.sent.trace.count(); perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); perf->pktRecv = m_stats.rcvr.recvd.trace.count(); @@ -9647,9 +9648,10 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) // 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; + const time_point start_time = m_stats.tsStartTime; g_snd_logger.state.tsNow = tnow; - g_snd_logger.state.usElapsed = count_microseconds(tnow - m_stats.tsStartTime); + g_snd_logger.state.usElapsed = count_microseconds(tnow - start_time); g_snd_logger.state.usSRTT = m_iSRTT; g_snd_logger.state.usRTTVar = m_iRTTVar; g_snd_logger.state.msSndBuffSpan = buffdelay_ms; @@ -11038,7 +11040,8 @@ 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 + + time_point start_time = m_stats.tsStartTime; + int64_t timestamp = (count_microseconds(steady_clock::now() - start_time) / 60000000) + distractor + correction; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; From ed4b6cf68d05376bfa8622f3c1f66a5dd0f5d3c8 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 12 Mar 2025 16:35:04 +0100 Subject: [PATCH 131/174] Fixed pedantic C++ v2 --- test/test_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_common.cpp b/test/test_common.cpp index df85cefd6..4b3c80d84 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -118,7 +118,7 @@ TEST(SRTAPI, SyncRendezvousHangs) (sockaddr*)&peer_sa, sizeof peer_sa), SRT_ERROR); close_thread.join(); - ASSERT_LE(duration, 1); + ASSERT_LE(duration, 1lu); // Worst case it will compare uint64_t against uint32_t on 32-bit systems. } TEST(SRTAPI, RapidClose) From 8cfd3de519bf2b0e532ecb0bfa486dd32f528b4a Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 13 Mar 2025 10:19:04 +0100 Subject: [PATCH 132/174] Fixed pedantic error in a test --- test/test_file_transmission.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 5937f43ce..35e1be7d9 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -288,10 +288,10 @@ TEST(FileTransmission, Setup46) buffer[i] = dis(mtrd); } - EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), int(SIZE)) << srt_getlasterror_str(); char resultbuf[SIZE]; - EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), int(SIZE)) << srt_getlasterror_str(); // It should use the maximum payload size per packet reported from the option. int payloadsize_back = 0; From 67ad15133716dd8e3f85ba4bf244ed880747bdb4 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 13 Mar 2025 10:45:51 +0100 Subject: [PATCH 133/174] Again --- test/test_file_transmission.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 35e1be7d9..77116854c 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -386,10 +386,10 @@ TEST(FileTransmission, Setup66) buffer[i] = dis(mtrd); } - EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_send(sock_acp, buffer, int(SIZE)), SIZE) << srt_getlasterror_str(); char resultbuf[SIZE]; - EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_recv(sock_clr, resultbuf, int(SIZE)), SIZE) << srt_getlasterror_str(); // It should use the maximum payload size per packet reported from the option. int payloadsize_back = 0; From 9190f1175aa3c3039246ff82c750b9d296c257f8 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 13 Mar 2025 10:51:36 +0100 Subject: [PATCH 134/174] Wrong fix --- test/test_file_transmission.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 77116854c..40f299e73 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -386,10 +386,10 @@ TEST(FileTransmission, Setup66) buffer[i] = dis(mtrd); } - EXPECT_EQ(srt_send(sock_acp, buffer, int(SIZE)), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), int(SIZE)) << srt_getlasterror_str(); char resultbuf[SIZE]; - EXPECT_EQ(srt_recv(sock_clr, resultbuf, int(SIZE)), SIZE) << srt_getlasterror_str(); + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), int(SIZE)) << srt_getlasterror_str(); // It should use the maximum payload size per packet reported from the option. int payloadsize_back = 0; From d8debf2a281d1a54243b46dd20e9f17386335bb3 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 13 Mar 2025 15:02:10 +0100 Subject: [PATCH 135/174] Removed unreachable code --- srtcore/api.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 24e99e64a..8fe50df2a 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3541,8 +3541,6 @@ SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } - - return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } // [[using locked(m_ControlLock)]] From 79cd03223733ab23bfef507702ac040870cea50c Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 10:49:12 +0100 Subject: [PATCH 136/174] TESTING CI: ABI Checks with displayable HTML --- .github/workflows/abi.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml index 2c05cc06b..d0fa712e0 100644 --- a/.github/workflows/abi.yml +++ b/.github/workflows/abi.yml @@ -59,3 +59,8 @@ jobs: echo "ABI/API Compatibility check failed with value $?" exit $RES fi + - name: HTML Preview + id: html_preview + uses: pavi2410/html-preview-action@v2 + with: + html_file: 'compat_reports/libsrt/1_to_0/compat_report.html' From 912125ee1f3d1301c22687a05432285679708786 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 12:00:12 +0100 Subject: [PATCH 137/174] Moved accept wait before epoll in ConnectNonBlocking --- test/test_bonding.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index c45cb6847..6fbfa0845 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -904,7 +904,12 @@ TEST(Bonding, ConnectNonBlocking) ThreadName::set("TEST_A"); - cout << "[A] Waiting for accept\n"; + cout << "[A] Accept delay until connect done...\n"; + // Delay with executing accept to keep the peer in "in progress" + // connection state. + connect_passed.get_future().get(); + + cout << "[A] Accept: go on - waiting on epoll to accept\n"; // This can wait in infinity; worst case it will be killed in process. int uwait_res = srt_epoll_uwait(lsn_eid, ev, 3, -1); @@ -916,13 +921,6 @@ TEST(Bonding, ConnectNonBlocking) EXPECT_NE(ev[0].events & ev_in_bit, 0); bool have_also_update = ev[0].events & SRT_EPOLL_UPDATE; - cout << "[A] Accept delay until connect done...\n"; - // Delay with executing accept to keep the peer in "in progress" - // connection state. - connect_passed.get_future().get(); - - cout << "[A] Accept: go on\n"; - sockaddr_any adr; SRTSOCKET accept_id = srt_accept(g_listen_socket, adr.get(), &adr.len); @@ -958,7 +956,6 @@ TEST(Bonding, ConnectNonBlocking) } accept_passed.set_value(); - cout << "[A] Waitig on epoll for close (up to 5s)\n"; // Wait up to 5s for an error srt_epoll_uwait(lsn_eid, ev, 3, 5000); From 205bc94789bdfa1efab2efcc70a9f309703a4f62 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 12:44:33 +0100 Subject: [PATCH 138/174] Withdrawn HTML preview for ABI (doesn't work) --- .github/workflows/abi.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml index d0fa712e0..2c05cc06b 100644 --- a/.github/workflows/abi.yml +++ b/.github/workflows/abi.yml @@ -59,8 +59,3 @@ jobs: echo "ABI/API Compatibility check failed with value $?" exit $RES fi - - name: HTML Preview - id: html_preview - uses: pavi2410/html-preview-action@v2 - with: - html_file: 'compat_reports/libsrt/1_to_0/compat_report.html' From e017b149281cc117ff0f0a906a2c98b663aedcf6 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 13:30:59 +0100 Subject: [PATCH 139/174] TEST: Check if ConnectNonBlocking can prevent connection by delaying listening --- test/test_bonding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 6fbfa0845..3b424d5f7 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -866,7 +866,6 @@ TEST(Bonding, ConnectNonBlocking) EXPECT_NE(srt_bind(g_listen_socket, sa.get(), sa.size()), -1); const int yes = 1; srt_setsockflag(g_listen_socket, SRTO_GROUPCONNECT, &yes, sizeof yes); - EXPECT_NE(srt_listen(g_listen_socket, 5), -1); int lsn_eid = srt_epoll_create(); int lsn_events = SRT_EPOLL_IN | SRT_EPOLL_ERR | SRT_EPOLL_UPDATE; @@ -908,6 +907,7 @@ TEST(Bonding, ConnectNonBlocking) // Delay with executing accept to keep the peer in "in progress" // connection state. connect_passed.get_future().get(); + EXPECT_NE(srt_listen(g_listen_socket, 5), -1); cout << "[A] Accept: go on - waiting on epoll to accept\n"; From 8fc42a6e813fe4dd40acb36be351a1cbcf741eab Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 16:42:20 +0100 Subject: [PATCH 140/174] Merged and fixed dev-fix-2980 --- srtcore/channel.cpp | 19 +++++++++++++------ srtcore/channel.h | 26 +++++++++++++------------- srtcore/common.h | 32 +++++++++++++++++++++++++++++++- srtcore/core.cpp | 8 ++++---- srtcore/core.h | 6 +++--- srtcore/packet.h | 4 ++-- srtcore/queue.cpp | 4 ++-- srtcore/queue.h | 4 ++-- 8 files changed, 70 insertions(+), 33 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 81dbc4d35..8b35bbfef 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -721,12 +721,19 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const w_addr.len = namelen; } -int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& source_addr SRT_ATR_UNUSED) const +int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const CNetworkInterface& source_ni SRT_ATR_UNUSED) const { #if ENABLE_HEAVY_LOGGING ostringstream dsrc; #ifdef SRT_ENABLE_PKTINFO - dsrc << " sourceIP=" << (m_bBindMasked && !source_addr.isany() ? source_addr.str() : "default"); + if (m_bBindMasked && !source_ni.address.isany()) + { + dsrc << " sourceNI=" << source_ni.str(); + } + else + { + dsrc << " sourceNI=default"; + } #endif LOGC(kslog.Debug, @@ -809,15 +816,15 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka // Note that even if PKTINFO is desired, the first caller's packet will be sent // without ancillary info anyway because there's no "peer" yet to know where to send it. char mh_crtl_buf[sizeof(CMSGNodeIPv4) + sizeof(CMSGNodeIPv6)]; - if (m_bBindMasked && source_addr.family() != AF_UNSPEC && !source_addr.isany()) + if (m_bBindMasked && source_ni.address.family() != AF_UNSPEC && !source_ni.address.isany()) { - if (!setSourceAddress(mh, mh_crtl_buf, source_addr)) + if (!setSourceAddress(mh, mh_crtl_buf, source_ni)) { - LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_addr.family() << ", NOT setting."); + LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_ni.address.family() << ", NOT setting."); } else { - HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_addr.str()); + HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_ni.str()); have_set_src = true; } } diff --git a/srtcore/channel.h b/srtcore/channel.h index e12310001..573554cb6 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -117,7 +117,7 @@ class CChannel /// @param [in] src source address to sent on an outgoing packet (if not ANY) /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, srt::CPacket& packet, const sockaddr_any& src) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet, const CNetworkInterface& src) const; /// Receive a packet from the channel and record the source address. /// @param [in] addr pointer to the source address. @@ -205,7 +205,7 @@ class CChannel cmsghdr hdr; }; - sockaddr_any getTargetAddress(const msghdr& msg) const + CNetworkInterface getTargetAddress(const msghdr& msg) const { // Loop through IP header messages cmsghdr* cmsg; @@ -219,33 +219,33 @@ class CChannel { in_pktinfo dest_ip; memcpy(&dest_ip, CMSG_DATA(cmsg), sizeof(struct in_pktinfo)); - return sockaddr_any(dest_ip.ipi_addr, 0); + return CNetworkInterface(dest_ip.ipi_addr, dest_ip.ipi_ifindex); } if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { in6_pktinfo dest_ip; memcpy(&dest_ip, CMSG_DATA(cmsg), sizeof(struct in6_pktinfo)); - return sockaddr_any(dest_ip.ipi6_addr, 0); + return CNetworkInterface(dest_ip.ipi6_addr, dest_ip.ipi6_ifindex); } } // Fallback for an error - return sockaddr_any(m_BindAddr.family()); + return CNetworkInterface(); } // IMPORTANT!!! This function shall be called EXCLUSIVELY just before // calling ::sendmsg function. It uses a static buffer to supply data // for the call, and it's stated that only one thread is trying to // use a CChannel object in sending mode. - bool setSourceAddress(msghdr& mh, char *buf, const sockaddr_any& adr) const + bool setSourceAddress(msghdr& mh, char *buf, const CNetworkInterface& ni) const { // In contrast to an advice followed on the net, there's no case of putting // both IPv4 and IPv6 ancillary data, case we could have them. Only one // IP version is used and it's the version as found in @a adr, which should // be the version used for binding. - if (adr.family() == AF_INET) + if (ni.address.family() == AF_INET) { mh.msg_control = (void *) buf; mh.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); @@ -254,16 +254,16 @@ class CChannel cmsg_send->cmsg_level = IPPROTO_IP; cmsg_send->cmsg_type = IP_PKTINFO; cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - + in_pktinfo pktinfo; - pktinfo.ipi_ifindex = 0; - pktinfo.ipi_spec_dst = adr.sin.sin_addr; + pktinfo.ipi_ifindex = ni.interface_index; + pktinfo.ipi_spec_dst = ni.address.sin.sin_addr; memcpy(CMSG_DATA(cmsg_send), &pktinfo, sizeof(in_pktinfo)); return true; } - if (adr.family() == AF_INET6) + if (ni.address.family() == AF_INET6) { mh.msg_control = buf; mh.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); @@ -274,8 +274,8 @@ class CChannel cmsg_send->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); in6_pktinfo* pktinfo = (in6_pktinfo*) CMSG_DATA(cmsg_send); - pktinfo->ipi6_ifindex = 0; - pktinfo->ipi6_addr = adr.sin6.sin6_addr; + pktinfo->ipi6_ifindex = ni.interface_index; + pktinfo->ipi6_addr = ni.address.sin6.sin6_addr; return true; } diff --git a/srtcore/common.h b/srtcore/common.h index 39774b00b..2f8cfb08a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -54,6 +54,7 @@ modified by #define INC_SRT_COMMON_H #include +#include #include #include #ifndef _WIN32 @@ -95,7 +96,36 @@ modified by #define SRT_STATIC_ASSERT(cond, msg) #endif -#include +namespace srt +{ + +struct CNetworkInterface +{ + sockaddr_any address; + int interface_index; + + template + CNetworkInterface(const InAddrType& sa, int index) + : address(sa, 0) + , interface_index(index) + { + } + + CNetworkInterface() // blank fallback + : address(AF_UNSPEC) + , interface_index(0) + { + } + + std::string str() const + { + std::ostringstream buf; + buf << address.str() << "/" << interface_index; + return buf.str(); + } +}; + +} namespace srt_logging { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6a4b8d565..b953512f1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3684,7 +3684,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) EConnectStatus cst = CONN_CONTINUE; // This is a temporary place to store the DESTINATION IP from the incoming packet. // We can't record this address yet until the cookie-confirmation is done, for safety reasons. - sockaddr_any use_source_adr(serv_addr.family()); + CNetworkInterface use_source_adr; while (!m_bClosing) //while (!m_bClosing && !m_bBroken) @@ -6032,7 +6032,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } } -bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW +bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const CNetworkInterface& hsaddr, CHandShake& w_hs) ATR_NOTHROW { // Send the response to the peer, see listen() for more discussions // about this. @@ -9771,7 +9771,7 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) return true; } -bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, sockaddr_any& w_src_addr) +bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, CNetworkInterface& w_src_addr) { int payload = 0; bool probe = false; @@ -11294,7 +11294,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // because this happens still in the frames of the listener socket. Only // when processing switches to the newly spawned accepted socket can the // address be recorded in its m_SourceAddr field. - sockaddr_any use_source_addr = packet.udpDestAddr(); + CNetworkInterface use_source_addr = packet.udpDestAddr(); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as diff --git a/srtcore/core.h b/srtcore/core.h index 2ff02844b..c4319bf55 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -603,7 +603,7 @@ class CUDT /// @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); - bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW; + bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const CNetworkInterface& hsaddr, CHandShake& w_hs) ATR_NOTHROW; /// Write back to the hs structure the data after they have been /// negotiated by acceptAndRespond. @@ -1138,7 +1138,7 @@ class CUDT /// /// @retval true A packet was extracted for sending, the socket should be rechecked at @a nexttime /// @retval false Nothing was extracted for sending, @a nexttime should be ignored - bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr); + bool packData(CPacket& packet, time_point& nexttime, CNetworkInterface& src_addr); /// Also excludes srt::CUDTUnited::m_GlobControlLock. SRT_ATTR_EXCLUDES(m_RcvTsbPdStartupLock, m_StatsLock, m_RecvLock, m_RcvLossLock, m_RcvBufferLock) @@ -1239,7 +1239,7 @@ class CUDT CSndQueue* m_pSndQueue; // packet sending queue CRcvQueue* m_pRcvQueue; // packet receiving queue sockaddr_any m_PeerAddr; // peer address - sockaddr_any m_SourceAddr; // override UDP source address with this one when sending + CNetworkInterface m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue diff --git a/srtcore/packet.h b/srtcore/packet.h index c59d660a6..2feaab1f9 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -309,7 +309,7 @@ class CPacket /// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP). uint32_t getMsgTimeStamp() const; - sockaddr_any udpDestAddr() const { return m_DestAddr; } + CNetworkInterface udpDestAddr() const { return m_DestAddr; } #ifdef SRT_DEBUG_TSBPD_WRAP // Receiver static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; // 27 bit fast wraparound for tests (~2m15s) @@ -348,7 +348,7 @@ class CPacket int32_t m_extra_pad; bool m_data_owned; - sockaddr_any m_DestAddr; + CNetworkInterface m_DestAddr; size_t m_zCapacity; protected: diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index d11200f46..6bd58415d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -584,7 +584,7 @@ void* srt::CSndQueue::worker(void* param) // pack a packet from the socket CPacket pkt; steady_clock::time_point next_send_time; - sockaddr_any source_addr; + CNetworkInterface source_addr; const bool res = u->packData((pkt), (next_send_time), (source_addr)); // Check if extracted anything to send @@ -608,7 +608,7 @@ void* srt::CSndQueue::worker(void* param) return NULL; } -int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const sockaddr_any& src) +int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const CNetworkInterface& src) { // send out the packet immediately (high priority), this is a control packet // NOTE: w_packet is passed by mutable reference because this function will do diff --git a/srtcore/queue.h b/srtcore/queue.h index cd76c5fc2..4d444f525 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -434,7 +434,7 @@ class CSndQueue /// @param [in,ref] packet packet to be sent out /// @param [in] src The source IP address (details above) /// @return Size of data sent out. - int sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& src); + int sendto(const sockaddr_any& addr, CPacket& packet, const CNetworkInterface& src); /// Get the IP TTL. /// @param [in] ttl IP Time To Live. @@ -610,7 +610,7 @@ struct CMultiplexer , m_pChannel(NULL) , m_pTimer(NULL) , m_iPort(0) - , m_iIPversion(0) + , m_iIPversion(AF_UNSPEC) , m_iRefCount(1) , m_iID(-1) { From 0edccb36185e5023388679c6a8efb24d24632123 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 14 Mar 2025 17:16:14 +0100 Subject: [PATCH 141/174] Changed paths to checked code by Sonar --- sonar-project.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 4bdaccdb2..afe611c78 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -28,8 +28,9 @@ sonar.links.issue=https://github.com/Haivision/srt/issues # ===================================================== # SQ standard properties -sonar.sources=srtcore/,apps/,common/,examples/,haicrypt/,scripts/,testing/ -sonar.tests=test/ +sonar.sources=srtcore/,common/,haicrypt/,scripts/ +sonar.tests=test/,testing/,apps/,examples/ +sonar.verbose=true # Properties specific to the C/C++ analyzer: #sonar.cfamily.build-wrapper-output=_build/sonar-output From 42dc6a8775896182f33f5e86464245a69d6d5478 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Mar 2025 16:12:26 +0100 Subject: [PATCH 142/174] [MAINT] Added information about the CM process as a marker change in development branch. --- docs/dev/developers-guide.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/dev/developers-guide.md b/docs/dev/developers-guide.md index 3b0e1c7b6..074d338d9 100644 --- a/docs/dev/developers-guide.md +++ b/docs/dev/developers-guide.md @@ -276,6 +276,32 @@ does its best to make sure that no option is missing. Note that some options might be provided by an external dependent script (like `build-gmock`) and therefore mistakenly added to the list. +### Release and configuration management + +The release management follows the rules of the "Git Flow" tool. + +The `master` branch should contain the version on the latest stable release. + +The ongoing development should be merged to `development` branch. + +New feature branches should be drawn off `development` branch. They should use +the `feature/` prefix. + +Finished feature branches should be merged back to `development`. + +Once a release is defined, a new release branch is drawn off `development` +and it has a name with `release/` prefix. All bugfix branches for this +release are merged to this branch, and then `development` branch updated +with this fix. + +Once the release reaches stability, it is merged to `master`. Any +hotfixes are merged to the release branch, and then `master` is updated, +then so is the `development` branch. + +The `git-flow` tool can be used to support this workflow, but it can be +also implemented manually. + + [git-setup]: https://help.github.com/articles/set-up-git [github-issues]: https://github.com/Haivision/srt/issues [github-guide-prs]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork From 85373e85ed5076e9de0cdff08fccadc96e8adf52 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Mar 2025 16:23:24 +0100 Subject: [PATCH 143/174] [MAINT] Added information about the CM process as first change in dev branch. --- docs/dev/developers-guide.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/dev/developers-guide.md b/docs/dev/developers-guide.md index 3b0e1c7b6..2802ab975 100644 --- a/docs/dev/developers-guide.md +++ b/docs/dev/developers-guide.md @@ -276,6 +276,32 @@ does its best to make sure that no option is missing. Note that some options might be provided by an external dependent script (like `build-gmock`) and therefore mistakenly added to the list. +### Release and configuration management + +The release management follows the rules of the "Git Flow" tool. + +The `master` branch should contain the version on the latest stable release. + +The ongoing development should be merged to `dev` branch. + +New feature branches should be drawn off `dev` branch. They should use +`feature/` prefix. + +Finished feature branches should be merged back to `dev`. + +Once a release is defined, a new release branch is drawn off `dev` +and it has a name with `release/` prefix. All bugfix branches for this +release are merged to this branch, and then `dev` branch updated +with this fix. + +Once the release reaches stability, it is merged to `master`. Any +hotfixes are merged to the release branch, and then `master` is updated, +then so is the `dev` branch. + +The `git-flow` tool can be used to support this workflow, but it can be +also implemented manually. + + [git-setup]: https://help.github.com/articles/set-up-git [github-issues]: https://github.com/Haivision/srt/issues [github-guide-prs]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork From 7148e85fe7d66a9c66d31125232c8e4aadb9b55a Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 18 Mar 2025 16:32:01 +0100 Subject: [PATCH 144/174] [FIX] Calculate correctly max payload size per UDP packet (#2677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FIX] Calculate correctly max payload size per UDP packet * Applied verification of the payload size fit. Added tests. Updated documentation * Fixed minimum MSS to 116. Fixed some other bux * Fixed test ipv6 to use promise-future for synchronization * Fixed filtering-out IPv6 tests for Travis * Fixed tests that should require IPv6 enabled * Added extra v6-to-v6 test to check correct payload size * Added an API function to control the payload size * Removed BytesPacketsCount. Shifted header size to a function parameter * Added documentation for srt_getmaxpayloadsize --------- Co-authored-by: Mikołaj Małecki Co-authored-by: stevomatthews --- apps/srt-file-transmit.cpp | 7 +- apps/transmitmedia.cpp | 74 +++++++++++- apps/transmitmedia.hpp | 1 + docs/API/API-functions.md | 112 ++++++++++++++---- docs/API/API-socket-options.md | 156 +++++++++++++++++++++---- srtcore/api.cpp | 50 +++++++- srtcore/api.h | 2 + srtcore/buffer_tools.cpp | 4 +- srtcore/core.cpp | 73 ++++++++++-- srtcore/core.h | 2 + srtcore/group.cpp | 17 +-- srtcore/packet.h | 18 ++- srtcore/packetfilter_api.h | 2 +- srtcore/socketconfig.cpp | 87 ++++++++++---- srtcore/socketconfig.h | 4 +- srtcore/srt.h | 20 +++- srtcore/srt_c_api.cpp | 26 +++-- test/test_fec_rebuilding.cpp | 8 +- test/test_file_transmission.cpp | 200 +++++++++++++++++++++++++++++++- test/test_ipv6.cpp | 170 +++++++++++++++++++++++++-- test/test_socket_options.cpp | 2 +- testing/srt-test-mpbond.cpp | 2 +- testing/testmedia.cpp | 82 +++++++++++-- testing/testmedia.hpp | 1 + 24 files changed, 986 insertions(+), 134 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..e29ae3248 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -180,7 +180,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) return 2; } - cfg.chunk_size = stoul(Option(params, "1456", o_chunk)); + cfg.chunk_size = stoul(Option(params, "0", o_chunk)); cfg.skip_flushing = Option(params, false, o_no_flush); cfg.bw_report = stoi(Option(params, "0", o_bwreport)); cfg.stats_report = stoi(Option(params, "0", o_statsrep)); @@ -681,8 +681,11 @@ int main(int argc, char** argv) // // Set global config variables // - if (cfg.chunk_size != SRT_LIVE_MAX_PLSIZE) + if (cfg.chunk_size != 0) transmit_chunk_size = cfg.chunk_size; + else + transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET; + transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf); transmit_bw_report = cfg.bw_report; transmit_stats_report = cfg.stats_report; diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 862875fa2..0a656493e 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -44,7 +44,7 @@ bool g_stats_are_printed_to_stdout = false; bool transmit_total_stats = false; unsigned long transmit_bw_report = 0; unsigned long transmit_stats_report = 0; -unsigned long transmit_chunk_size = SRT_LIVE_MAX_PLSIZE; +unsigned long transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET6; class FileSource: public Source { @@ -179,6 +179,40 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } + unsigned int max_payload_size = 0; + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + if (host_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (adapter != "" && adapter != host) + { + adapter_sa = CreateAddr(adapter); + + if (adapter_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; + if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { m_tsbpdmode = false; @@ -195,11 +229,17 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + if (transmit_chunk_size > max_payload_size) + Error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assign the others here. m_options = par; @@ -263,6 +303,21 @@ bool SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + // we do one client connection at a time, // so close the listener. srt_close(m_bindsock); @@ -431,6 +486,19 @@ void SrtCommon::ConnectClient(string host, int port) Error("srt_connect"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + stat = ConfigurePost(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePost"); diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 527f005d9..a4812d815 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -42,6 +42,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; SRTSOCKET m_sock = SRT_INVALID_SOCK; SRTSOCKET m_bindsock = SRT_INVALID_SOCK; bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; } diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 2fdb8a661..63da7f651 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -19,6 +19,7 @@ | [srt_bind_acquire](#srt_bind_acquire) | Acquires a given UDP socket instead of creating one | | [srt_getsockstate](#srt_getsockstate) | Gets the current status of the socket | | [srt_getsndbuffer](#srt_getsndbuffer) | Retrieves information about the sender buffer | +| [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) | Retrieves the information about the maximum payload size in a single packet | | [srt_close](#srt_close) | Closes the socket or group and frees all used resources | | | | @@ -28,7 +29,7 @@ |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | | [srt_listen](#srt_listen) | Sets up the listening state on a socket | | [srt_accept](#srt_accept) | Accepts a connection; creates/returns a new socket or group ID | -| [srt_accept_bond](#srt_accept_bond) | Accepts a connection pending on any sockets passed in the `listeners` array
of `nlisteners` size | +| [srt_accept_bond](#srt_accept_bond) | Accepts a connection pending on any sockets passed in the `listeners` array
of `nlisteners` size | | [srt_listen_callback](#srt_listen_callback) | Installs/executes a callback hook on a socket created to handle the incoming connection
on a listening socket | | [srt_connect](#srt_connect) | Connects a socket or a group to a remote party with a specified address and port | | [srt_connect_bind](#srt_connect_bind) | Same as [`srt_bind`](#srt_bind) then [`srt_connect`](#srt_connect) if called with socket [`u`](#u) | @@ -153,26 +154,27 @@ Since SRT v1.5.0.

Rejection Reasons

-| *Rejection Reason* | *Since* | *Description* | -|:-------------------------------------------- |:--------- |:-------------------------------------------------------------------------------------------------------------- | -| [SRT_REJ_UNKNOWN](#SRT_REJ_UNKNOWN) | 1.3.4 | A fallback value for cases when there was no connection rejected | -| [SRT_REJ_SYSTEM](#SRT_REJ_SYSTEM) | 1.3.4 | A system function reported a failure | -| [SRT_REJ_PEER](#SRT_REJ_PEER) | 1.3.4 | The connection has been rejected by peer, but no further details are available | -| [SRT_REJ_RESOURCE](#SRT_REJ_RESOURCE) | 1.3.4 | A problem with resource allocation (usually memory) | -| [SRT_REJ_ROGUE](#SRT_REJ_ROGUE) | 1.3.4 | The data sent by one party to another cannot be properly interpreted | -| [SRT_REJ_BACKLOG](#SRT_REJ_BACKLOG) | 1.3.4 | The listener's backlog has exceeded | -| [SRT_REJ_IPE](#SRT_REJ_IPE) | 1.3.4 | Internal Program Error | -| [SRT_REJ_CLOSE](#SRT_REJ_CLOSE) | 1.3.4 | The listener socket received a request as it is being closed | -| [SRT_REJ_VERSION](#SRT_REJ_VERSION) | 1.3.4 | A party did not satisfy the minimum version requirement that had been set up for a connection | -| [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | 1.3.4 | Rendezvous cookie collision | -| [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | 1.3.4 | Both parties have defined a passphrase for connection and they differ | -| [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one connection party has set up a password | -| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | 1.3.4 | The value for [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties | -| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | 1.3.4 | The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | -| [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | -| [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | -| [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | -| | | | +| *Rejection Reason* | *Since* | *Description* | +|:-------------------------------------------- |:--------- |:---------------------------------------------------------------------------------------------------------------- | +| [SRT_REJ_UNKNOWN](#SRT_REJ_UNKNOWN) | 1.3.4 | A fallback value for cases when there was no connection rejected | +| [SRT_REJ_SYSTEM](#SRT_REJ_SYSTEM) | 1.3.4 | A system function reported a failure | +| [SRT_REJ_PEER](#SRT_REJ_PEER) | 1.3.4 | The connection has been rejected by peer, but no further details are available | +| [SRT_REJ_RESOURCE](#SRT_REJ_RESOURCE) | 1.3.4 | A problem with resource allocation (usually memory) | +| [SRT_REJ_ROGUE](#SRT_REJ_ROGUE) | 1.3.4 | The data sent by one party to another cannot be properly interpreted | +| [SRT_REJ_BACKLOG](#SRT_REJ_BACKLOG) | 1.3.4 | The listener's backlog has exceeded | +| [SRT_REJ_IPE](#SRT_REJ_IPE) | 1.3.4 | Internal Program Error | +| [SRT_REJ_CLOSE](#SRT_REJ_CLOSE) | 1.3.4 | The listener socket received a request as it is being closed | +| [SRT_REJ_VERSION](#SRT_REJ_VERSION) | 1.3.4 | A party did not satisfy the minimum version requirement that had been set up for a connection | +| [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | 1.3.4 | Rendezvous cookie collision | +| [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | 1.3.4 | Both parties have defined a passphrase for connection and they differ | +| [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one connection party has set up a password | +| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | 1.3.4 | The value for [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on the peer | +| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | 1.3.4 | The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option is set differently on the peer | +| [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | +| [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | +| [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | +| [SRT_REJ_CONFIG](#SRT_REJ_CONFIG) | 1.6.0 | The connection was rejected because settings on both parties are in collision and cannot negotiate common values | +| | | | See the full list in [Rejection Reason Codes](./rejection-codes.md). @@ -294,6 +296,7 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne * [srt_bind_acquire](#srt_bind_acquire) * [srt_getsockstate](#srt_getsockstate) * [srt_getsndbuffer](#srt_getsndbuffer) +* [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) * [srt_close](#srt_close) @@ -541,6 +544,58 @@ 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) + +--- + +### srt_getmaxpayloadsize + +``` +int srt_getmaxpayloadsize(SRTSOCKET u); +``` + +Returns the maximum number of bytes that fit in a single packet. Useful only in +live mode (when `SRTO_TSBPDMODE` is true). The socket must be bound (see +[srt_bind](#srt_bind)) or connected (see [srt_connect](#srt_connect)) +to use this function. Note that in case when the socket is bound to an IPv6 +wildcard address and it is dual-stack (`SRTO_IPV6ONLY` is set to false), this +function returns the correct value only if the socket is connected, otherwise +it will return the value always as if the connection was made from an IPv6 peer +(including when you call it on a listening socket). + +This function is only useful for the application to check if it is able to use +a payload of certain size in the live mode, or after connection, if the application +can send payloads of certain size. This is useful only in assertions, as if the +[`SRTO_PAYLOADSIZE`](API_socket-options.md#SRTO_PAYLOADSIZE) option is to be +set to a non-default value (for which the one returned by this function is the +maximum value), this option should be modified before connection and on both +parties, regarding the settings applied on the socket. + +The returned value is the maximum number of bytes that can be put in a single +packet regarding: + +* The current MTU size (`SRTO_MSS`) +* The IP version (IPv4 or IPv6) +* The `SRTO_CRYPTOMODE` setting (bytes reserved for AEAD authentication tag) +* The `SRTO_PACKETFILTER` setting (bytes reserved for extra field in a FEC control packet) + +With default options this value should be 1456 for IPv4 and 1444 for IPv6. + + +| Returns | | +|:----------------------------- |:------------------------------------------------- | +| The maximum payload size (>0) | If succeeded | +| `SRT_ERROR` | Usage error | +| | | + +| Errors | | +|:--------------------------------------- |:----------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | Socket [`u`](#u) is not bound | +| | | + + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -3077,6 +3132,21 @@ and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by the application. +#### SRT_REJ_CRYPTO + +Settings for `SRTO_CRYPTOMODE` on both parties are not compatible with one another. +See [`SRTO_CRYPTOMODE`](API-socket-options.md#SRTO_CRYPTOMODE) for details. + +#### SRT_REJ_CONFIG + +Settings for various transmission parameters that are supposed to be negotiated +during the handshake (in order to agree upon a common value) are under restrictions +that make finding common values for them impossible. Cases include: + +* `SRTO_PAYLOADSIZE`, which is nonzero in live mode, is set to a value that +exceeds the free space in a single packet that results from the value of the +negotiated MSS value + [: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 c550845a7..a02d443a8 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -972,20 +972,88 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `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 -value independently. During a handshake the parties exchange MSS values, and -the lowest is used. - -*Generally on the internet MSS is 1500 by default. This is the maximum -size of a UDP packet and can be only decreased, unless you have some unusual -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`. +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 116.. | RW | GSD | + +Maximum Segment Size. This value represents the maximum size of a UDP packet +sent by the system. Therefore the value of `SRTO_MSS` must not exceed the +values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer +allocation and rate calculation using a packet counter that assumes fully filled +packets. + +This value is a sum of: + +* IP header (20 bytes for IPv4, or 32 bytes for IPv6) +* UDP header (8 bytes) +* SRT header (16 bytes) +* remaining space (as the maximum payload size available for a packet) + +For the default 1500 the "remaining space" is effectively 1456 for IPv4 +and 1444 for IPv6, although it can be limited by nondefault values of some +other socket options. + +Note that the IP version used here is not the domain of the underlying UDP +socket, but the in-transmission IP version. This is effectively IPv4 in the +following cases: + +* when the current socket's binding address is of IPv4 domain +* when the peer's address is an IPv6-mapped-IPv4 address + +The IPv6 transmission case is assumed only if the peer's address is a true IPv6 +address (not IPv4 mapped). It is then not possible to determine the payload +size limit until the connection is established. SRT operations that must +allocate any resources according to this value prior to connecting will assume +IPv4 transmission because this way, in the worst case, they allocate more space +than needed. + +This value can be set on both connection parties independently, but after +connection `SRTO_MSS` gets a negotiated value, which is the lesser of the two. +If this effective value is too small for either of the connection peers, the +connection is rejected (or late-rejected on the caller side). + +This value then controls: + +* The maximum size of the payload in a single UDP packet ("remaining space"). + +* The size of the memory space allocated for a single packet in the sender +and receiver buffers. This value is equal to "SRT header" + "remaining space" +in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it +is that some buffer resources are allocated prior to the connection, so this +value must fit both IPv4 and IPv6 for buffer memory allocation. + +The default value of 1500 corresponds to the standard MTU size for network +devices. It is recommended that this value be set to the maximum MTU size of +the network device that you will use for the connection. + +The recommendations for the value of `SRTO_MSS` differ between file and live modes. + +In live mode a single call to the `srt_send\*` function may only send data that +fit in one packet. This size is defined by the `SRTO_PAYLOADSIZE` option +(defult: 1316), and it is also the size of the data in a single UDP packet. To +save memory space, you may want then to set `SRTO_MSS` in live mode to a value +for which the "remaining space" matches the `SRTO_PAYLOADSIZE` value (for the +default value of 1316 this will be 1360 for IPv4 and 1372 for IPv6). For +security reasons, this is not done by default: it may potentially lead to the +inability to read an incoming UDP packet if its size is for some reason bigger +than the negotiated MSS, which may in turn lead to unpredictable behaviour and +hard-to-detect errors. You should set such a value only if the peer is trusted +(that is, you can be certain that you will never receive an oversized UDP +packet over the link used for the connection). You should also consider the +limitations of `SRTO_PAYLOADSIZE`. + +In file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for sending a single packet, and therefore bigger portions of data are +internally split into smaller portions, each one using the maximum available +"remaining space". The best value of `SRTO_MSS` for this case is then equal to +the current network device's MTU size. Setting a greater value is possible +(maximum for the system API is 65535), but it may lead to packet fragmentation +on the system level. This is highly unwanted in SRT because: + +* SRT also performs its own fragmentation, so it would be counter-productive +* It would use more system resources to no advantage +* SRT is unaware of it, so the resulting statistics would be slightly misleading + +System-level packet fragmentation cannot be reliably turned off, +so the safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) @@ -1082,7 +1150,7 @@ Cases when negotiation succeeds: | 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: +In these cases the configuration is rejected with `SRT_REJ_FILTER` code: | Peer A | Peer B | Error reason |-----------------------|---------------------|-------------------------- @@ -1137,14 +1205,60 @@ 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 | \* | 0.. \* | W | GSD | +| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | RW | GSD | + +Sets the mode that determines the limitations on how data is sent, including the maximum +size of payload data sent within a single UDP packet. This option can be only set prior +to connecting, but it can be read also after the connection has been established. + +The default value is 1316 in live mode (which is default) and 0 in file mode (when file +mode is set through the `SRTO_TRANSTYPE` option). + +In file mode (`SRTO_PAYLOADSIZE` = 0) the call to `srt_send\*` is not limited +to the size of a single packet. If necessary, the supplied data will be split +into multiple pieces, each fitting into a single UDP packet. Every data payload +(except the last one in the stream or in the message) will use the maximum +space available in a UDP packet, as determined by `SRTO_MSS` and other settings +that may influence this size (such as [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) +and [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)). + +Also when this option is set to 0 prior to connecting, then reading this option +from a connected socket will return the maximum size of the payload that fits +in a single packet according to the current connection parameters. + +In live mode (`SRTO_PAYLOADSIZE` > 0) the value defines the maximum size of: + +* a single call to a sending function (`srt_send\*`) +* the payload supplied in each data packet + +as well as the minimum size of the buffer used for the `srt_recv\*` call. + +This value can be set to a greater value than the default 1316, but the maximum +possible value is limited by the following factors: + +* 1500 bytes is the default MSS (see [`SRTO_MSS`](#SRTO_MSS)), including headers, which occupy: + * 20 bytes for IPv4, or 32 bytes for IPv6 + * 8 bytes for UDP + * 16 bytes for SRT + +This alone gives a limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be further decreased in the following cases: + +* 4 bytes reserved for FEC, if you use the built in FEC packet filter (see [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER)) +* 16 bytes reserved for the authentication tag, if you use AES GCM (see [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)) + +**WARNING**: The party setting the options will reject a value that is too +large, but note that not every limitation can be checked prior to connection. +This includes: -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. +* the MSS value defined by a peer, which may override the MSS set by an agent +* the in-transmission IP version (see [SRTO_MSS](#SRTO_MSS) for details) -For Live mode: Default value is 1316, but can be increased up to 1456. Note that -with the `SRTO_PACKETFILTER` option additional header space is usually required, -which decreases the maximum possible value for `SRTO_PAYLOADSIZE`. +These values also influence the "remaining space" in the packet to be used for +payload. If during the handshake it turns out that this "remaining space" is +less than the value set for `SRTO_PAYLOADSIZE` (including when it remains with +the default value), the connection will be rejected with the `SRT_REJ_SETTINGS` +code. For File mode: Default value is 0 and it's recommended not to be changed. diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 1108c0a1d..bd25a0f28 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3325,7 +3325,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; - m.m_pRcvQueue->init(128, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + // We can't use maxPayloadSize() because this value isn't valid until the connection is established. + // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. + const size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + + HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 + << " plsize=" << payload_size << " hsize=" << 1024); + m.m_pRcvQueue->init(128, payload_size, 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. @@ -4425,6 +4432,47 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) } } +int srt::CUDT::getMaxPayloadSize(SRTSOCKET id) +{ + return uglobal().getMaxPayloadSize(id); +} + +int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) +{ + CUDTSocket* s = locateSocket(id); + if (!s) + { + return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL); + } + + if (s->m_SelfAddr.family() == AF_UNSPEC) + { + return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND); + } + + int fam = s->m_SelfAddr.family(); + CUDT& u = s->core(); + + std::string errmsg; + int extra = u.m_config.extraPayloadReserve((errmsg)); + if (extra == -1) + { + LOGP(aclog.Error, errmsg); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL); + } + + // Prefer transfer IP version, if defined. This is defined after + // the connection is established. Note that the call is rejected + // if the socket isn't bound, be it explicitly or implicitly by + // calling srt_connect(). + if (u.m_TransferIPVersion != AF_UNSPEC) + fam = u.m_TransferIPVersion; + + int payload_size = u.m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(fam) - extra; + + return payload_size; +} + //////////////////////////////////////////////////////////////////////////////// namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index d0249ca10..4e66d4246 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -428,6 +428,8 @@ class CUDTUnited CUDTSocket* locateSocket_LOCKED(SRTSOCKET u); CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn); + int getMaxPayloadSize(SRTSOCKET u); + #if ENABLE_BONDING CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); CUDTGroup* acquireSocketsGroup(CUDTSocket* s); diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index e809b952e..054231915 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -102,12 +102,12 @@ void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); } -CRateEstimator::CRateEstimator(int /*family*/) +CRateEstimator::CRateEstimator(int family) : m_iInRatePktsCount(0) , m_iInRateBytesCount(0) , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) - , m_iFullHeaderSize(CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE) + , m_iFullHeaderSize(CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE) {} void CRateEstimator::setInputRateSmpPeriod(int period) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f3812cc4a..0e84cda1d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -273,6 +273,7 @@ void srt::CUDT::construct() m_pSndQueue = NULL; m_pRcvQueue = NULL; + m_TransferIPVersion = AF_UNSPEC; // Will be set after connection m_pSNode = NULL; m_pRNode = NULL; @@ -904,7 +905,7 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // Initial sequence number, loss, acknowledgement, etc. void srt::CUDT::clearData() { - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -3108,7 +3109,7 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) // XXX Using less maximum payload size of IPv4 and IPv6; this is only about the payload size // for live. - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET6 - cfg.extra_size; if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, @@ -4748,15 +4749,33 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } + m_TransferIPVersion = m_PeerAddr.family(); + if (m_PeerAddr.family() == AF_INET6) + { + // Check if the m_PeerAddr's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(m_PeerAddr.sin6)) + m_TransferIPVersion = AF_INET; + } + // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; - HLOGC(cnlog.Debug, log << CONID() << "applyResponseSettings: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_CONFIG; + return false; + } + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; + const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -5747,16 +5766,31 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) try { + // XXX SND buffer may allocate more memory, but must set the size of a single + // packet that fits the transmission for the overall connection. For any mixed 4-6 + // connection it should be the less size, that is, for IPv6 + // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. const int authtag = getAuthTagSize(); SRT_ASSERT(m_iMaxSRTPayloadSize != 0); - - HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << m_iMaxSRTPayloadSize - << " snd-bufsize=" << 32 + SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); + // IMPORTANT: + // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent + // over the current connection - which means that if both parties are IPv6, then the maximum size + // is the one for IPv6 (1444). If any party is IPv4, this maximum size is 1456. + // The family as the first argument is something different - it's for the header size in order + // to calculate rate and statistics. + + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(m_TransferIPVersion); + SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); + + HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size + << " snd-bufsize=" << 32 << " TF-IPv" + << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") << " authtag=" << authtag); - m_pSndBuffer = new CSndBuffer(AF_INET, 32, m_iMaxSRTPayloadSize, authtag); + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iPeerISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space. @@ -5804,8 +5838,16 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // Uses the smaller MSS between the peers m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS); - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_CONFIG; + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -5830,6 +5872,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& rewriteHandshakeData(peer, (w_hs)); + m_TransferIPVersion = peer.family(); + if (peer.family() == AF_INET6) + { + // Check if the peer's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(peer.sin6)) + m_TransferIPVersion = AF_INET; + } + // Prepare all structures if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0)) @@ -7531,7 +7582,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(m_TransferIPVersion == AF_UNSPEC ? AF_INET : m_TransferIPVersion); { int32_t flight_span = getFlightSpan(); diff --git a/srtcore/core.h b/srtcore/core.h index ed250c641..17e08b0cc 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -254,6 +254,7 @@ class CUDT static int rejectReason(SRTSOCKET s); static int rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); + static int getMaxPayloadSize(SRTSOCKET u); public: // internal API // This is public so that it can be used directly in API implementation functions. @@ -1231,6 +1232,7 @@ class CUDT sockaddr_any m_PeerAddr; // peer address sockaddr_any m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address + int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 4c292cb89..6a868aaf9 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -236,7 +236,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); if (plsize == 0) - plsize = SRT_LIVE_MAX_PLSIZE; + plsize = CPacket::srtPayloadSize(data.agent.family()); // It is stated that the payload size // is taken from first, and every next one // will get the same. @@ -569,8 +569,8 @@ void CUDTGroup::deriveSettings(CUDT* u) IM(SRTO_FC, iFlightFlagSize); // Nonstandard - importTrivialOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); - importTrivialOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); + importTrivialOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); + importTrivialOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); IM(SRTO_LINGER, Linger); @@ -712,7 +712,7 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) case SRTO_SNDBUF: case SRTO_RCVBUF: - w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::UDP_HDR_SIZE)); + w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::udpHeaderSize(AF_INET))); break; case SRTO_LINGER: @@ -2640,7 +2640,7 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) // links and sending a single packet over these two links could be different. // These stats then don't make much sense in this form, this has to be // redesigned. We use the header size as per IPv4, as it was everywhere. - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(AF_INET); memset(perf, 0, sizeof *perf); @@ -3762,7 +3762,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Only live streaming is supported - if (len > SRT_LIVE_MAX_PLSIZE) + // Also - as the group may use potentially IPv4 and IPv6 connections + // in the same group, use the size that fits both + if (len > SRT_MAX_PLSIZE_AF_INET6) { LOGC(gslog.Error, log << "grp/send(backup): buffer size=" << len << " exceeds maximum allowed in live mode"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -4198,7 +4200,8 @@ void CUDTGroup::internalKeepalive(SocketData* gli) } } -CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_LIVE_MAX_PLSIZE /*, 1000*/); +// Use the bigger size of SRT_MAX_PLSIZE to potentially fit both IPv4/6 +CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_MAX_PLSIZE_AF_INET /*, 1000*/); // Forwarder needed due to class definition order int32_t CUDTGroup::generateISN() diff --git a/srtcore/packet.h b/srtcore/packet.h index 5a6d6eb15..c59d660a6 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -371,16 +371,24 @@ class CPacket static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH_E_SIZE * sizeof(uint32_t) - // Can also be calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr). - static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. - - static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE; +private: // Do not disclose ingredients to the public + static const size_t UDP_HDR_SIZE = 8; // 8 bytes of UDP { u16 sport, dport, len, csum }. + static const size_t IPv4_HDR_SIZE = 20; // 20 bytes IPv4 + static const size_t IPv6_HDR_SIZE = 32; // 32 bytes IPv6 +public: + static inline size_t udpHeaderSize(int family) + { + return UDP_HDR_SIZE + (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE); + } + static inline size_t srtPayloadSize(int family) + { + return ETH_MAX_MTU_SIZE - (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE) - UDP_HDR_SIZE - HDR_SIZE; + } // Maximum transmission unit size. 1500 in case of Ethernet II (RFC 1191). static const size_t ETH_MAX_MTU_SIZE = 1500; // Maximum payload size of an SRT packet. - static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE; // Packet interface char* data() { return m_pcData; } diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 3bfba7c76..ef0d8867f 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -66,7 +66,7 @@ struct SrtFilterInitializer struct SrtPacket { uint32_t hdr[SRT_PH_E_SIZE]; - char buffer[SRT_LIVE_MAX_PLSIZE]; + char buffer[SRT_MAX_PLSIZE_AF_INET]; // Using this as the bigger one (this for AF_INET6 is smaller) size_t length; SrtPacket(size_t size): length(size) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index a0c8a6a47..56a7c73e6 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -57,7 +57,10 @@ namespace srt int RcvBufferSizeOptionToValue(int val, int flightflag, int mss) { // Mimimum recv buffer size is 32 packets - const int mssin_size = mss - CPacket::UDP_HDR_SIZE; + // We take the size per packet as maximum allowed for AF_INET, + // as we don't know which one is used, and this requires more + // space than AF_INET6. + const int mssin_size = mss - CPacket::udpHeaderSize(AF_INET); int bufsize; if (val > mssin_size * CSrtConfig::DEF_MIN_FLIGHT_PKT) @@ -90,9 +93,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + using namespace srt_logging; const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + const int handshake_size = CHandShake::m_iContentSize + (sizeof(uint32_t) * SRT_HS_E_SIZE); + const int minval = int(CPacket::udpHeaderSize(AF_INET6) + CPacket::HDR_SIZE + handshake_size); + if (ival < minval) + { + LOGC(kmlog.Error, log << "SRTO_MSS: minimum value allowed is " << minval << " = [IPv6][UDP][SRT] headers + minimum SRT handshake"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } co.iMSS = ival; @@ -130,7 +139,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.bytesPerPkt(); } }; @@ -144,6 +153,16 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iRcvBufSize = srt::RcvBufferSizeOptionToValue(val, co.iFlightFlagSize, co.iMSS); + const int mssin_size = co.bytesPerPkt(); + + 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; } }; @@ -621,9 +640,13 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (val > SRT_LIVE_MAX_PLSIZE) + // We don't know at this point, how bit the payloadsize can be set, + // so we limit it to the biggest value of the two. + // When this payloadsize would be then too big to be used with given MSS and IPv6, + // this problem should be reported appropriately from srt_connect and srt_bind calls. + if (val > SRT_MAX_PLSIZE_AF_INET) { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU."); + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_MAX_PLSIZE_AF_INET << ", maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -826,7 +849,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET - fc.extra_size; if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Warn, @@ -999,40 +1022,54 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } -bool CSrtConfig::payloadSizeFits(size_t val, int /*ip_family*/, std::string& w_errmsg) ATR_NOTHROW +int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW { + int resv = 0; + if (!this->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(this->sPacketFilterConfig.str(), fc)) + if (!ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) { // Break silently. This should not happen - w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; - return false; + w_info = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return -1; } - const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << fc.extra_size - << " required for packet filter header"; - w_errmsg = log.str(); - return false; - } + resv += fc.extra_size; + w_info = "Packet Filter"; + } + + if (this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + resv += HAICRYPT_AUTHTAG_MAX; + if (!w_info.empty()) + w_info += " and "; + w_info += "AES_GCM mode"; } - // Not checking AUTO to allow defaul 1456 bytes. - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX))) + return resv; +} + +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + int resv = extraPayloadReserve((w_errmsg)); + if (resv == -1) + return false; + + size_t valmax = CPacket::srtPayloadSize(ip_family) - resv; + + if (val > valmax) { std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE - << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."; + log << "SRTO_PAYLOADSIZE: value " << val << "exceeds " << valmax + << " bytes"; + if (!w_errmsg.empty()) + log << " as limited by " << w_errmsg; + w_errmsg = log.str(); return false; } diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 0f6c79887..a07c5028b 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -355,7 +355,9 @@ struct CSrtConfig: CSrtMuxerConfig // This function returns the number of bytes that are allocated // for a single packet in the sender and receiver buffer. - int bytesPerPkt() const { return iMSS - int(CPacket::UDP_HDR_SIZE); } + int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } + + int extraPayloadReserve(std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 71ac2c3af..1fa2756b2 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -288,9 +288,17 @@ typedef enum SRT_TRANSTYPE // This is for MPEG TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE. static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS -// This is the maximum payload size for Live mode, should you have a different -// payload type than MPEG TS. -static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) +// DEPRECATED. Use one of these below instead. +SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) + +// These constants define the maximum size of the payload +// in a single UDP packet, depending on the IP version, and +// with the default socket options, that is: +// * default 1500 bytes of MTU (see SRTO_MSS) +// * without FEC packet filter (see SRTO_PACKETFILTER) +// * without AEAD through AES-GCM (see SRTO_CRYPTOMODE) +static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) +static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; @@ -546,10 +554,11 @@ enum SRT_REJECT_REASON SRT_REJ_CONGESTION, // incompatible congestion-controller type SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group - SRT_REJ_TIMEOUT, // connection timeout + SRT_REJ_TIMEOUT = 16,// connection timeout #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif + SRT_REJ_CONFIG = 18, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; @@ -781,6 +790,9 @@ SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCK SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API int srt_getmaxpayloadsize(SRTSOCKET sock); + + typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; typedef struct SRT_MsgCtrl_ diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 031030cd7..8262b8ae0 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -169,6 +169,11 @@ int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } +int srt_getmaxpayloadsize(SRTSOCKET u) +{ + return CUDT::getMaxPayloadSize(u); +} + int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); } @@ -415,6 +420,9 @@ int srt_clock_type() return SRT_SYNC_CLOCK; } +// NOTE: crypto mode is defined regardless of the setting of +// ENABLE_AEAD_API_PREVIEW symbol. This can only block the symbol, +// but it doesn't change the symbol layout. const char* const srt_rejection_reason_msg [] = { "Unknown or erroneous", "Error in system calls", @@ -432,10 +440,9 @@ const char* const srt_rejection_reason_msg [] = { "Congestion controller type collision", "Packet Filter settings error", "Group settings collision", - "Connection timeout" -#ifdef ENABLE_AEAD_API_PREVIEW - ,"Crypto mode" -#endif + "Connection timeout", + "Crypto mode", + "Invalid configuration" }; // Deprecated, available in SRT API. @@ -456,14 +463,14 @@ extern const char* const srt_rejectreason_msg[] = { srt_rejection_reason_msg[13], srt_rejection_reason_msg[14], srt_rejection_reason_msg[15], - srt_rejection_reason_msg[16] -#ifdef ENABLE_AEAD_API_PREVIEW - , srt_rejection_reason_msg[17] -#endif + srt_rejection_reason_msg[16], + srt_rejection_reason_msg[17], + srt_rejection_reason_msg[18] }; const char* srt_rejectreason_str(int id) { + using namespace srt_logging; if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -471,7 +478,10 @@ const char* srt_rejectreason_str(int id) static const size_t ra_size = Size(srt_rejection_reason_msg); if (size_t(id) >= ra_size) + { + HLOGC(gglog.Error, log << "Invalid rejection code #" << id); return srt_rejection_reason_msg[0]; + } return srt_rejection_reason_msg[id]; } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 3a7cb0791..e7b27fb1b 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -58,7 +58,7 @@ class TestFECRebuilding: public srt::Test source.emplace_back(new CPacket); CPacket& p = *source.back(); - p.allocate(SRT_LIVE_MAX_PLSIZE); + p.allocate(SRT_MAX_PLSIZE_AF_INET); uint32_t* hdr = p.getHeader(); @@ -774,7 +774,7 @@ TEST_F(TestFECRebuilding, Prepare) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -795,7 +795,7 @@ TEST_F(TestFECRebuilding, NoRebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -872,7 +872,7 @@ TEST_F(TestFECRebuilding, Rebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index bfd668ac7..40f299e73 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -18,6 +18,7 @@ #endif #include "srt.h" +#include "netinet_any.h" #include #include @@ -29,7 +30,7 @@ //#pragma comment (lib, "ws2_32.lib") -TEST(Transmission, FileUpload) +TEST(FileTransmission, Upload) { srt::TestInit srtinit; srtinit.HandlePerTestOptions(); @@ -210,3 +211,200 @@ TEST(Transmission, FileUpload) remove("file.target"); } + +TEST(FileTransmission, Setup46) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_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_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), int(SIZE)) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), int(SIZE)) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + +} + +TEST(FileTransmission, Setup66) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + // Require that the connection allows both IP versions. + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + ASSERT_NE(srt_setsockflag(sock_lsn, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_any sa_lsn(AF_INET6); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.hport(connect_port); + bind_res = srt_bind(sock_lsn, sa_lsn.get(), sa_lsn.size()); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_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_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + std::cout << "Connecting to: " << sa.str() << std::endl; + + int connect_result = srt_connect(sock_clr, sa.get(), sa.size()); + ASSERT_EQ(connect_result, 0) << srt_getlasterror_str(); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), SRT_ERROR); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), int(SIZE)) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), int(SIZE)) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET6); + std::cout << "Payload size: " << payloadsize_back << std::endl; + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + // We use the same data size that fit in 1 payload IPv4, but not IPv6. + // Therefore sending should be here split into two packets. + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 2); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 2); + +} diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index ee11292b0..9843a0303 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -1,12 +1,16 @@ #include +#include #include +#include #include "gtest/gtest.h" #include "test_env.h" #include "srt.h" +#include "sync.h" #include "netinet_any.h" using srt::sockaddr_any; +using namespace srt::sync; class TestIPv6 : public srt::Test @@ -36,6 +40,11 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); + + m_CallerStarted.reset(new std::promise); + m_ReadyCaller.reset(new std::promise); + m_ReadyAccept.reset(new std::promise); + } void teardown() override @@ -47,20 +56,71 @@ class TestIPv6 } public: + + void SetupFileMode() + { + int val = SRTT_FILE; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + ASSERT_NE(srt_setsockflag(m_listener_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + } + + int m_CallerPayloadSize = 0; + int m_AcceptedPayloadSize = 0; + + std::unique_ptr> m_CallerStarted, m_ReadyCaller, m_ReadyAccept; + + // "default parameter" version. Can't use default parameters because this goes + // against binding parameters. Nor overloading. void ClientThread(int family, const std::string& address) { + return ClientThreadFlex(family, address, true); + } + + void ClientThreadFlex(int family, const std::string& address, bool shouldwork) + { + std::future ready_accepter = m_ReadyAccept->get_future(); + sockaddr_any sa (family); sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; + + m_CallerStarted->set_value(); 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"); + if (shouldwork) + { + // Version with expected success + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + + m_ReadyCaller->set_value(); + + PrintAddresses(m_caller_sock, "CALLER"); + + if (connect_res == SRT_ERROR) + { + std::cout << "Connect failed - [UNLOCK]\n"; + srt_close(m_listener_sock); + } + else + { + std::cout << "Connect succeeded, [FUTURE-WAIT...]\n"; + ready_accepter.wait(); + } + } + else + { + // Version with expected failure + EXPECT_EQ(connect_res, SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_CONFIG); + srt_close(m_listener_sock); + } + std::cout << "Connect: exit\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -68,24 +128,34 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; - std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + // Printing may happen from different threads, avoid intelining. + std::ostringstream sout; + sout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + std::cout << sout.str(); } sockaddr_any DoAccept() { sockaddr_any sc1; + // Make sure the caller started + m_CallerStarted->get_future().wait(); + std::cout << "DoAccept: caller started, proceeding to accept\n"; + SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); 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); EXPECT_NE(sn.get_addr(), nullptr); + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); + + m_ReadyCaller->get_future().wait(); if (sn.get_addr() != nullptr) { @@ -94,6 +164,9 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + std::cout << "DoAccept: ready accept - promise SET\n"; + m_ReadyAccept->set_value(); + srt_close(accepted_sock); return sn; } @@ -197,3 +270,86 @@ TEST_F(TestIPv6, v6_calls_v4) client.join(); } +TEST_F(TestIPv6, plsize_v6) +{ + SRTST_REQUIRES(IPv6); + + SetupFileMode(); + + 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"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1444); // == 1500 - 32[IPv6] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1444); + + client.join(); +} + +TEST_F(TestIPv6, plsize_v4) +{ + SetupFileMode(); + + 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"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1456); // == 1500 - 20[IPv4] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1456); + + client.join(); +} + +TEST_F(TestIPv6, plsize_faux_v6) +{ + SRTST_REQUIRES(IPv6); + + using namespace std::chrono; + SetupFileMode(); + + 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); + + int oversize = 1450; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &oversize, sizeof (int)), -1); + + std::thread client(&TestIPv6::ClientThreadFlex, this, AF_INET6, "::1", false); + + // Set on sleeping to make sure that the thread started. + // Sleeping isn't reliable so do a dampened spinlock here. + // This flag also confirms that the caller acquired the mutex and will + // unlock it for CV waiting - so we can proceed to notifying it. + m_CallerStarted->get_future().wait(); + + // Just in case of a test failure, kick CV to avoid deadlock + std::cout << "TEST: [PROMISE-SET]\n"; + m_ReadyAccept->set_value(); + + client.join(); +} diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index fe27bb387..3a5e68b88 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -342,7 +342,7 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {}, O | W | G | S | D | O | O }, { SRTO_MININPUTBW, "SRTO_MININPUTBW", RestrictionType::POST, sizeof(int64_t), int64_t(0), INT64_MAX, int64_t(0), int64_t(200000), {int64_t(-1)}, R | W | G | S | D | O | O }, { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {}, R | W | G | S | D | O | O }, - { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75}, R | W | G | S | D | O | O }, + { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 116, 65536, 1500, 1400, {-1, 0, 75}, R | W | G | S | D | O | O }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {}, R | W | G | S | D | O | M }, { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101}, R | W | G | S | D | O | O }, //SRTO_PACKETFILTER diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index 157fa9728..8a5143ddf 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -238,7 +238,7 @@ int main( int argc, char** argv ) return 2; } - size_t chunk = SRT_LIVE_MAX_PLSIZE; + size_t chunk = SRT_MAX_PLSIZE_AF_INET; // state the bigger size // Now run the loop try diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2c36a4d12..aedf48f0e 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -391,6 +391,41 @@ void SrtCommon::InitParameters(string host, string path, map par) { m_mode = par.at("mode"); } + + size_t max_payload_size = 0; + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + if (host_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (adapter != "") + { + adapter_sa = CreateAddr(adapter); + + if (adapter_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; + SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) { @@ -444,16 +479,19 @@ void SrtCommon::InitParameters(string host, string path, map par) // That's kinda clumsy, but it must rely on the defaults. // Default mode is live, so check if the file mode was enforced - if (par.count("transtype") == 0 || par["transtype"] != "file") + if ((par.count("transtype") == 0 || par["transtype"] != "file") + && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - // If the Live chunk size was nondefault, enforce the size. - if (transmit_chunk_size != SRT_LIVE_DEF_PLSIZE) - { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + if (transmit_chunk_size > max_payload_size) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); - par["payloadsize"] = Sprint(transmit_chunk_size); - } + par["payloadsize"] = Sprint(transmit_chunk_size); + } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; } // Assigning group configuration from a special "groupconfig" attribute. @@ -568,6 +606,21 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + #if ENABLE_BONDING if (m_sock & SRTGROUP_MASK) { @@ -1405,6 +1458,19 @@ void SrtCommon::ConnectClient(string host, int port) transmit_error_storage.clear(); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + Verb() << " connected."; stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 36a649130..f44e9a768 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -103,6 +103,7 @@ class SrtCommon std::string m_mode; std::string m_adapter; std::map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; std::vector m_group_nodes; std::string m_group_type; std::string m_group_config; From aa91b885f58d50c25952f89cb2dc386c0ecd6834 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 18 Mar 2025 16:47:18 +0100 Subject: [PATCH 145/174] [MAINT] Updated github actions to work on dev branch (#3147) Co-authored-by: Mikolaj Malecki --- .github/workflows/abi.yml | 4 ++-- .github/workflows/android.yaml | 4 ++-- .github/workflows/codeql.yml | 4 ++-- .github/workflows/cxx11-macos.yaml | 4 ++-- .github/workflows/cxx11-ubuntu.yaml | 4 ++-- .github/workflows/cxx11-win.yaml | 4 ++-- .github/workflows/iOS.yaml | 4 ++-- .github/workflows/s390x-focal.yaml | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml index 2c05cc06b..4bd01bb59 100644 --- a/.github/workflows/abi.yml +++ b/.github/workflows/abi.yml @@ -2,9 +2,9 @@ name: ABI checks on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] env: SRT_BASE: v1.5.0 diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 0af85fda3..f1599e411 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -2,9 +2,9 @@ name: Android on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] jobs: build: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4a337b6ca..f7f417259 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,9 @@ name: "CodeQL" on: push: - branches: [ "master", "experimental/socket-groups" ] + branches: [ "master", "dev" ] pull_request: - branches: [ "master" ] + branches: [ "master", "dev" ] jobs: analyze: diff --git a/.github/workflows/cxx11-macos.yaml b/.github/workflows/cxx11-macos.yaml index e3e6b1f8a..b6ca2ca5e 100644 --- a/.github/workflows/cxx11-macos.yaml +++ b/.github/workflows/cxx11-macos.yaml @@ -2,9 +2,9 @@ name: cxx11 on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] jobs: build: diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 5566ed801..78721a7e3 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -2,9 +2,9 @@ name: cxx11 on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] types: [opened, synchronize, reopened] jobs: build: diff --git a/.github/workflows/cxx11-win.yaml b/.github/workflows/cxx11-win.yaml index f1554053d..4f96ebd40 100644 --- a/.github/workflows/cxx11-win.yaml +++ b/.github/workflows/cxx11-win.yaml @@ -2,9 +2,9 @@ name: cxx11 on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] jobs: build: diff --git a/.github/workflows/iOS.yaml b/.github/workflows/iOS.yaml index 0fb11542b..30e54167b 100644 --- a/.github/workflows/iOS.yaml +++ b/.github/workflows/iOS.yaml @@ -2,9 +2,9 @@ name: iOS on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] jobs: build: diff --git a/.github/workflows/s390x-focal.yaml b/.github/workflows/s390x-focal.yaml index f1b6c7508..d91991456 100644 --- a/.github/workflows/s390x-focal.yaml +++ b/.github/workflows/s390x-focal.yaml @@ -2,9 +2,9 @@ name: QEMU to run s390x-focal on: push: - branches: [ master ] + branches: [ "master", "dev" ] pull_request: - branches: [ master ] + branches: [ "master", "dev" ] jobs: Tests: From ddc5d0a6a8729a2fe0977cd8f62b722a7d2e5205 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 10:33:01 +0100 Subject: [PATCH 146/174] [MAINT] Added script for manual ABI checks (#3148) Co-authored-by: Mikolaj Malecki --- .gitmodules | 3 ++ scripts/abi-check.tcl | 68 +++++++++++++++++++++++++++++++ submodules/abi-compliance-checker | 1 + 3 files changed, 72 insertions(+) create mode 100644 .gitmodules create mode 100755 scripts/abi-check.tcl create mode 160000 submodules/abi-compliance-checker diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..0c81e9233 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "submodules/abi-compliance-checker"] + path = submodules/abi-compliance-checker + url = https://github.com/lvc/abi-compliance-checker.git diff --git a/scripts/abi-check.tcl b/scripts/abi-check.tcl new file mode 100755 index 000000000..15a585de3 --- /dev/null +++ b/scripts/abi-check.tcl @@ -0,0 +1,68 @@ +#!/usr/bin/tclsh + +set here [file dirname $argv0] ;# points to [git top]/scripts +set top [file normalize $here/..] + +set abichecker [file normalize [file join $top submodules abi-compliance-checker abi-compliance-checker.pl]] + +if { ![file exists $abichecker] } { + puts stderr "Please update submodules first (compliance checker not found in the current view)" + exit 1 +} + +# Check if abi-dumper is installed + +set abidumper [auto_execok abi-dumper] +if {$abidumper == ""} { + set installer "" + foreach ii {zypper dnf apt} { + if {[auto_execok $ii] != ""} { + set installer $ii + break + } + } + if {$installer != ""} { + puts stderr "ABI dumper not installed. Use such commands to install\n" + puts stderr " $installer install abi-dumper" + } else { + puts stderr "ABI dumper not installed. Find out how to install abi-dumper in your system." + } + exit 1 +} + +# Arguments: +# [directory-with-base] [directory-with-pr] + +# NOTE: ABI dump will be done in every build directory as specified. + +proc generate-abi-dump {directory lver} { + set wd [pwd] + cd $directory + global top + + # You should have libsrt.so in this directory. + # Use [glob] to use exception if no file exists + glob libsrt.so + + exec >@stdout 2>@stderr abi-dumper libsrt.so -o libsrt-abi.dump -public-headers $top/srtcore -lver $lver + cd $wd +} + +set olddir [lindex $argv 0] +set newdir [lindex $argv 1] + +if {![file isdirectory $olddir] || ![file isdirectory $newdir]} { + puts stderr "Wrong arguments. Required build directory" + exit 1 +} + +generate-abi-dump $olddir 1 +generate-abi-dump $newdir 0 + +set res [catch {exec >@stdout 2>@stderr $abichecker -l libsrt -old $olddir/libsrt-abi.dump -new $newdir/libsrt-abi.dump} out] + +if {$res} { + puts stderr "ABI compat problems found!!!\nSee the HTML report" +} + + diff --git a/submodules/abi-compliance-checker b/submodules/abi-compliance-checker new file mode 160000 index 000000000..7c175c45a --- /dev/null +++ b/submodules/abi-compliance-checker @@ -0,0 +1 @@ +Subproject commit 7c175c45a8ba9ac41b8e47d8ebbab557b623b18e From 53a09f3ca73cd4a51a1926c6a6b0b2fa85a6ac48 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 10:40:00 +0100 Subject: [PATCH 147/174] [core] Fixed: closing socket should mark and signal so that srt_connect call can exit immediately (#2032) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core] Fixed: closing socket should mark and signal so that srt_connect call can exit immediately * Fixed problem: test if socket is in this blocking-connecting state before changing the flag. Otherwise it would confuse the closing function when used on a connected socket * Added UT for the closing case * Fixed unnecessary condition. Added more atomics (data race fix) * Fixes for atomic field * Minimized changes. Fixed incorrect test initialization * Some improvements and more debug for RapidClose test --------- Co-authored-by: Mikołaj Małecki --- srtcore/api.cpp | 37 ++++++++++++++++++ srtcore/core.cpp | 19 ++++++---- srtcore/core.h | 2 +- srtcore/queue.cpp | 5 +++ srtcore/queue.h | 2 + test/test_common.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 8 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index bd25a0f28..80e00a774 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2052,6 +2052,43 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); + + // The check for whether m_pRcvQueue isn't NULL is safe enough; + // it can either be NULL after socket creation and without binding + // and then once it's assigned, it's never reset to NULL even when + // destroying the socket. + CUDT& e = s->core(); + if (e.m_pRcvQueue && e.m_bConnecting && !e.m_bConnected) + { + // Workaround for a design flaw. + // It's to work around the case when the socket is being + // closed in another thread while it's in the process of + // connecting in the blocking mode, that is, it runs the + // loop in `CUDT::startConnect` whole time under the lock + // of CUDT::m_ConnectionLock and CUDTSocket::m_ControlLock + // this way blocking the `srt_close` API call from continuing. + // We are setting here the m_bClosing flag prematurely so + // that the loop may check this flag periodically and exit + // immediately if it's set. + // + // The problem is that this flag shall NOT be set in case + // when you have a CONNECTED socket because not only isn't it + // not a problem in this case, but also it additionally + // turns the socket in a "confused" state in which it skips + // vital part of closing itself and therefore runs an infinite + // loop when trying to purge the sender buffer of the closing + // socket. + // + // XXX Consider refax on CUDT::startConnect and removing the + // connecting loop there and replace the "blocking mode specific" + // connecting procedure with delegation to the receiver queue, + // which will be then common with non-blocking mode, and synchronize + // the blocking through a CV. + + e.m_bClosing = true; + e.m_pRcvQueue->kick(); + } + ScopedLock socket_cg(s->m_ControlLock); HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing from listening, closing CUDT)"); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0e84cda1d..af7aa81c1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3682,7 +3682,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // We can't record this address yet until the cookie-confirmation is done, for safety reasons. sockaddr_any use_source_adr(serv_addr.family()); - while (!m_bClosing) + while (!m_bClosing && !m_bBroken) { const steady_clock::time_point local_tnow = steady_clock::now(); const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load(); @@ -6953,7 +6953,8 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) << " DATA SIZE: " << size << " sched-SEQUENCE: " << seqno << " STAMP: " << BufferStamp(data, size)); - if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.time_since_epoch())) + time_point start_time = m_stats.tsStartTime; + if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(start_time.time_since_epoch())) { LOGC(aslog.Error, log << CONID() << "Wrong source time was provided. Sending is rejected."); @@ -7588,9 +7589,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) ScopedLock statsguard(m_StatsLock); - const steady_clock::time_point currtime = steady_clock::now(); + const time_point currtime = steady_clock::now(), + start_time = m_stats.tsStartTime; - perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); + perf->msTimeStamp = count_milliseconds(currtime - start_time); perf->pktSent = m_stats.sndr.sent.trace.count(); perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); perf->pktRecv = m_stats.rcvr.recvd.trace.count(); @@ -9697,9 +9699,10 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) // 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; + const time_point start_time = m_stats.tsStartTime; g_snd_logger.state.tsNow = tnow; - g_snd_logger.state.usElapsed = count_microseconds(tnow - m_stats.tsStartTime); + g_snd_logger.state.usElapsed = count_microseconds(tnow - start_time); g_snd_logger.state.usSRTT = m_iSRTT; g_snd_logger.state.usRTTVar = m_iRTTVar; g_snd_logger.state.msSndBuffSpan = buffdelay_ms; @@ -11088,7 +11091,8 @@ 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 + + time_point start_time = m_stats.tsStartTime; + int64_t timestamp = (count_microseconds(steady_clock::now() - start_time) / 60000000) + distractor + correction; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; @@ -11975,7 +11979,8 @@ int64_t srt::CUDT::socketStartTime(SRTSOCKET u) if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); - return count_microseconds(s->core().m_stats.tsStartTime.time_since_epoch()); + const time_point& start_time = s->core().m_stats.tsStartTime; + return count_microseconds(start_time.time_since_epoch()); } bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) diff --git a/srtcore/core.h b/srtcore/core.h index 17e08b0cc..f266fa998 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1185,7 +1185,7 @@ class CUDT private: // Trace struct CoreStats { - time_point tsStartTime; // timestamp when the UDT entity is started + atomic_time_point tsStartTime; // timestamp when the UDT entity is started stats::Sender sndr; // sender statistics stats::Receiver rcvr; // receiver statistics diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 6cb4faeb1..729fc2293 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1790,6 +1790,11 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } +void srt::CRcvQueue::kick() +{ + CSync::lock_notify_all(m_BufferCond, m_BufferLock); +} + void srt::CRcvQueue::storePktClone(int32_t id, const CPacket& pkt) { CUniqueSync passcond(m_BufferLock, m_BufferCond); diff --git a/srtcore/queue.h b/srtcore/queue.h index 48bedd9af..51b9950bd 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -553,6 +553,8 @@ class CRcvQueue void storePktClone(int32_t id, const CPacket& pkt); + void kick(); + private: sync::CSharedObjectPtr m_pListener; // pointer to the (unique, if any) listening UDT entity CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode diff --git a/test/test_common.cpp b/test/test_common.cpp index 1a8cca061..0c06ae68a 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -1,6 +1,12 @@ #include #include +#include + +#include +#include +#include + #include "gtest/gtest.h" #include "test_env.h" #include "utilities.h" @@ -71,3 +77,87 @@ TEST(CIPAddress, IPv4_in_IPv6_pton) test_cipaddress_pton(peer_ip, AF_INET6, ip); } + +TEST(SRTAPI, SyncRendezvousHangs) +{ + srt::TestInit srtinit; + int yes = 1; + + SRTSOCKET m_bindsock = srt_create_socket(); + ASSERT_NE(m_bindsock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_bindsock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes), 0); + + const int connection_timeout_ms = 1000; // rendezvous timeout is x10 hence 10seconds + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), 0); + + sockaddr_in local_sa={}; + local_sa.sin_family = AF_INET; + local_sa.sin_port = htons(9999); + local_sa.sin_addr.s_addr = INADDR_ANY; + + sockaddr_in peer_sa= {}; + peer_sa.sin_family = AF_INET; + peer_sa.sin_port = htons(9998); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &peer_sa.sin_addr), 1); + + uint64_t duration = 0; + + std::thread close_thread([&m_bindsock, &duration] { + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait till srt_rendezvous is called + auto start = std::chrono::steady_clock::now(); + srt_close(m_bindsock); + auto end = std::chrono::steady_clock::now(); + + duration = std::chrono::duration_cast(end - start).count(); + }); + + EXPECT_EQ(srt_rendezvous(m_bindsock, (sockaddr*)&local_sa, sizeof local_sa, + (sockaddr*)&peer_sa, sizeof peer_sa), SRT_ERROR); + + close_thread.join(); + ASSERT_LE(duration, 1lu); // Worst case it will compare uint64_t against uint32_t on 32-bit systems. +} + +TEST(SRTAPI, RapidClose) +{ + srt::TestInit srtinit; + using namespace std; + + + SRTSOCKET sock = srt_create_socket(); + std::condition_variable cv_start; + std::mutex cvm; + sync::atomic started(false), ended(false); + + std::thread connect_thread([&sock, &cv_start, &started, &ended] { + + // Nonexistent address + sockaddr_any sa = CreateAddr("localhost", 5555, AF_INET); + cerr << "[T] Start connect\n"; + started = true; + cv_start.notify_one(); + srt_connect(sock, sa.get(), sa.size()); + // It doesn't matter if it succeeds. Important is that it exits. + ended = true; + cerr << "[T] exit\n"; + }); + + std::unique_lock lk(cvm); + + // Wait until the thread surely starts + cerr << "Waiting for thread start...\n"; + while (!started) + cv_start.wait(lk); + + cerr << "Closing socket\n"; + srt_close(sock); + cerr << "Waiting 250ms\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + EXPECT_TRUE(ended); + + cerr << "Joining [T]\n"; + connect_thread.join(); +} From c879cc28bd17bf4c67c4820a96a4d1277171604b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:02:46 +0100 Subject: [PATCH 148/174] [core] Carry over also the interface number with PKTINFO (#2982) * [core] Carry over also the interface number with PKTINFO * Small refax and minor warning fix * Fixed printing sourceNI in logs --------- Co-authored-by: Mikolaj Malecki --- srtcore/channel.cpp | 19 +++++++++++++------ srtcore/channel.h | 26 +++++++++++++------------- srtcore/common.h | 32 +++++++++++++++++++++++++++++++- srtcore/core.cpp | 6 +++--- srtcore/core.h | 4 ++-- srtcore/packet.h | 4 ++-- srtcore/queue.cpp | 4 ++-- srtcore/queue.h | 4 ++-- 8 files changed, 68 insertions(+), 31 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 81dbc4d35..8b35bbfef 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -721,12 +721,19 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const w_addr.len = namelen; } -int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& source_addr SRT_ATR_UNUSED) const +int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const CNetworkInterface& source_ni SRT_ATR_UNUSED) const { #if ENABLE_HEAVY_LOGGING ostringstream dsrc; #ifdef SRT_ENABLE_PKTINFO - dsrc << " sourceIP=" << (m_bBindMasked && !source_addr.isany() ? source_addr.str() : "default"); + if (m_bBindMasked && !source_ni.address.isany()) + { + dsrc << " sourceNI=" << source_ni.str(); + } + else + { + dsrc << " sourceNI=default"; + } #endif LOGC(kslog.Debug, @@ -809,15 +816,15 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka // Note that even if PKTINFO is desired, the first caller's packet will be sent // without ancillary info anyway because there's no "peer" yet to know where to send it. char mh_crtl_buf[sizeof(CMSGNodeIPv4) + sizeof(CMSGNodeIPv6)]; - if (m_bBindMasked && source_addr.family() != AF_UNSPEC && !source_addr.isany()) + if (m_bBindMasked && source_ni.address.family() != AF_UNSPEC && !source_ni.address.isany()) { - if (!setSourceAddress(mh, mh_crtl_buf, source_addr)) + if (!setSourceAddress(mh, mh_crtl_buf, source_ni)) { - LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_addr.family() << ", NOT setting."); + LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_ni.address.family() << ", NOT setting."); } else { - HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_addr.str()); + HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_ni.str()); have_set_src = true; } } diff --git a/srtcore/channel.h b/srtcore/channel.h index e12310001..573554cb6 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -117,7 +117,7 @@ class CChannel /// @param [in] src source address to sent on an outgoing packet (if not ANY) /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, srt::CPacket& packet, const sockaddr_any& src) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet, const CNetworkInterface& src) const; /// Receive a packet from the channel and record the source address. /// @param [in] addr pointer to the source address. @@ -205,7 +205,7 @@ class CChannel cmsghdr hdr; }; - sockaddr_any getTargetAddress(const msghdr& msg) const + CNetworkInterface getTargetAddress(const msghdr& msg) const { // Loop through IP header messages cmsghdr* cmsg; @@ -219,33 +219,33 @@ class CChannel { in_pktinfo dest_ip; memcpy(&dest_ip, CMSG_DATA(cmsg), sizeof(struct in_pktinfo)); - return sockaddr_any(dest_ip.ipi_addr, 0); + return CNetworkInterface(dest_ip.ipi_addr, dest_ip.ipi_ifindex); } if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { in6_pktinfo dest_ip; memcpy(&dest_ip, CMSG_DATA(cmsg), sizeof(struct in6_pktinfo)); - return sockaddr_any(dest_ip.ipi6_addr, 0); + return CNetworkInterface(dest_ip.ipi6_addr, dest_ip.ipi6_ifindex); } } // Fallback for an error - return sockaddr_any(m_BindAddr.family()); + return CNetworkInterface(); } // IMPORTANT!!! This function shall be called EXCLUSIVELY just before // calling ::sendmsg function. It uses a static buffer to supply data // for the call, and it's stated that only one thread is trying to // use a CChannel object in sending mode. - bool setSourceAddress(msghdr& mh, char *buf, const sockaddr_any& adr) const + bool setSourceAddress(msghdr& mh, char *buf, const CNetworkInterface& ni) const { // In contrast to an advice followed on the net, there's no case of putting // both IPv4 and IPv6 ancillary data, case we could have them. Only one // IP version is used and it's the version as found in @a adr, which should // be the version used for binding. - if (adr.family() == AF_INET) + if (ni.address.family() == AF_INET) { mh.msg_control = (void *) buf; mh.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); @@ -254,16 +254,16 @@ class CChannel cmsg_send->cmsg_level = IPPROTO_IP; cmsg_send->cmsg_type = IP_PKTINFO; cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - + in_pktinfo pktinfo; - pktinfo.ipi_ifindex = 0; - pktinfo.ipi_spec_dst = adr.sin.sin_addr; + pktinfo.ipi_ifindex = ni.interface_index; + pktinfo.ipi_spec_dst = ni.address.sin.sin_addr; memcpy(CMSG_DATA(cmsg_send), &pktinfo, sizeof(in_pktinfo)); return true; } - if (adr.family() == AF_INET6) + if (ni.address.family() == AF_INET6) { mh.msg_control = buf; mh.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); @@ -274,8 +274,8 @@ class CChannel cmsg_send->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); in6_pktinfo* pktinfo = (in6_pktinfo*) CMSG_DATA(cmsg_send); - pktinfo->ipi6_ifindex = 0; - pktinfo->ipi6_addr = adr.sin6.sin6_addr; + pktinfo->ipi6_ifindex = ni.interface_index; + pktinfo->ipi6_addr = ni.address.sin6.sin6_addr; return true; } diff --git a/srtcore/common.h b/srtcore/common.h index ff84c3faf..f58eed4b9 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -54,6 +54,7 @@ modified by #define INC_SRT_COMMON_H #include +#include #include #include #ifndef _WIN32 @@ -95,7 +96,36 @@ modified by #define SRT_STATIC_ASSERT(cond, msg) #endif -#include +namespace srt +{ + +struct CNetworkInterface +{ + sockaddr_any address; + int interface_index; + + template + CNetworkInterface(const InAddrType& sa, int index) + : address(sa, 0) + , interface_index(index) + { + } + + CNetworkInterface() // blank fallback + : address(AF_UNSPEC) + , interface_index(0) + { + } + + std::string str() const + { + std::ostringstream buf; + buf << address.str() << "/" << interface_index; + return buf.str(); + } +}; + +} namespace srt_logging { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index af7aa81c1..75f0c04b5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3680,7 +3680,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) EConnectStatus cst = CONN_CONTINUE; // This is a temporary place to store the DESTINATION IP from the incoming packet. // We can't record this address yet until the cookie-confirmation is done, for safety reasons. - sockaddr_any use_source_adr(serv_addr.family()); + CNetworkInterface use_source_adr; while (!m_bClosing && !m_bBroken) { @@ -9736,7 +9736,7 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) return true; } -bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, sockaddr_any& w_src_addr) +bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, CNetworkInterface& w_src_addr) { int payload = 0; bool probe = false; @@ -11209,7 +11209,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // because this happens still in the frames of the listener socket. Only // when processing switches to the newly spawned accepted socket can the // address be recorded in its m_SourceAddr field. - sockaddr_any use_source_addr = packet.udpDestAddr(); + CNetworkInterface use_source_addr = packet.udpDestAddr(); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as diff --git a/srtcore/core.h b/srtcore/core.h index f266fa998..ea81646fa 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1130,7 +1130,7 @@ class CUDT /// /// @retval true A packet was extracted for sending, the socket should be rechecked at @a nexttime /// @retval false Nothing was extracted for sending, @a nexttime should be ignored - bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr); + bool packData(CPacket& packet, time_point& nexttime, CNetworkInterface& src_addr); /// Also excludes srt::CUDTUnited::m_GlobControlLock. SRT_ATTR_EXCLUDES(m_RcvTsbPdStartupLock, m_StatsLock, m_RecvLock, m_RcvLossLock, m_RcvBufferLock) @@ -1230,7 +1230,7 @@ class CUDT CSndQueue* m_pSndQueue; // packet sending queue CRcvQueue* m_pRcvQueue; // packet receiving queue sockaddr_any m_PeerAddr; // peer address - sockaddr_any m_SourceAddr; // override UDP source address with this one when sending + CNetworkInterface m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue diff --git a/srtcore/packet.h b/srtcore/packet.h index c59d660a6..2feaab1f9 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -309,7 +309,7 @@ class CPacket /// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP). uint32_t getMsgTimeStamp() const; - sockaddr_any udpDestAddr() const { return m_DestAddr; } + CNetworkInterface udpDestAddr() const { return m_DestAddr; } #ifdef SRT_DEBUG_TSBPD_WRAP // Receiver static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; // 27 bit fast wraparound for tests (~2m15s) @@ -348,7 +348,7 @@ class CPacket int32_t m_extra_pad; bool m_data_owned; - sockaddr_any m_DestAddr; + CNetworkInterface m_DestAddr; size_t m_zCapacity; protected: diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 729fc2293..d02f42646 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -584,7 +584,7 @@ void* srt::CSndQueue::worker(void* param) // pack a packet from the socket CPacket pkt; steady_clock::time_point next_send_time; - sockaddr_any source_addr; + CNetworkInterface source_addr; const bool res = u->packData((pkt), (next_send_time), (source_addr)); // Check if extracted anything to send @@ -608,7 +608,7 @@ void* srt::CSndQueue::worker(void* param) return NULL; } -int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const sockaddr_any& src) +int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const CNetworkInterface& src) { // send out the packet immediately (high priority), this is a control packet // NOTE: w_packet is passed by mutable reference because this function will do diff --git a/srtcore/queue.h b/srtcore/queue.h index 51b9950bd..5bb229cd5 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -420,7 +420,7 @@ class CSndQueue /// @param [in,ref] packet packet to be sent out /// @param [in] src The source IP address (details above) /// @return Size of data sent out. - int sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& src); + int sendto(const sockaddr_any& addr, CPacket& packet, const CNetworkInterface& src); /// Get the IP TTL. /// @param [in] ttl IP Time To Live. @@ -594,7 +594,7 @@ struct CMultiplexer , m_pChannel(NULL) , m_pTimer(NULL) , m_iPort(0) - , m_iIPversion(0) + , m_iIPversion(AF_UNSPEC) , m_iRefCount(1) , m_iID(-1) { From 881c5136154d65e4f3f117d8c660475c3ea72818 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:16:14 +0100 Subject: [PATCH 149/174] [core][API] Obtaining local interfaces to allow getting connected device name (#2989) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added check code for attempting rendezvous to self (in the application) * Added prevention of self-connecting to testing applications * Fixed incorrect implementation on Windows. Reading all families * Fixed external inet_pton for mingw * Moved Iphlpapi lib to test programs only * Some fixes for Windows * [core][API] Added srt_getsockdevname to obtain the device name from a connected socket * Enabled LOCALIF flag on C++11-win workflow * Fixed a case when the address is NULL (found on Cygwin) * Added printing device on listener in verbose mode * Enabled LOCALIF always on windows, not only MICROSOFT * Fixed Iphlpapi library depend declaration on Windows --------- Co-authored-by: Mikołaj Małecki --- .github/workflows/cxx11-win.yaml | 2 +- CMakeLists.txt | 13 +++++ apps/apputil.cpp | 38 ++++++++++++++ apps/apputil.hpp | 1 + srtcore/api.cpp | 51 +++++++++++++++++++ srtcore/api.h | 1 + srtcore/common.cpp | 85 ++++++++++++++++++++++++++++++++ srtcore/common.h | 8 +++ srtcore/core.h | 1 + srtcore/srt.h | 1 + srtcore/srt_c_api.cpp | 1 + testing/testmedia.cpp | 32 +++++++++++- 12 files changed, 231 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cxx11-win.yaml b/.github/workflows/cxx11-win.yaml index 4f96ebd40..ee2488666 100644 --- a/.github/workflows/cxx11-win.yaml +++ b/.github/workflows/cxx11-win.yaml @@ -17,7 +17,7 @@ jobs: - name: configure run: | md _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=OFF -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DUSE_CXX_STD=c++11 + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=OFF -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_LOCALIF_WIN32=ON -DUSE_CXX_STD=c++11 - name: build run: cd _build && cmake --build ./ --config Release --verbose - name: test diff --git a/CMakeLists.txt b/CMakeLists.txt index 847b6f23f..11f112540 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(SRT_USE_OPENSSL_STATIC_LIBS "Link OpenSSL libraries statically." OFF) 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_LOCALIF_WIN32 "Enable local interface check ability on Windows (adds Iphlpapi.lib dep)" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF) @@ -1103,6 +1104,10 @@ if (srt_libspec_shared) if (ENABLE_ENCRYPTION) target_link_libraries(${TARGET_srt}_shared PRIVATE ${SSL_LIBRARIES}) endif() + if (WIN32 AND ENABLE_LOCALIF_WIN32) + target_link_libraries(${TARGET_srt}_shared PRIVATE Iphlpapi) + add_definitions(-DSRT_ENABLE_LOCALIF_WIN32) + endif() if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PRIVATE ws2_32.lib) if (NOT (ENABLE_ENCRYPTION AND "${USE_ENCLIB}" STREQUAL "botan")) @@ -1124,6 +1129,10 @@ endif() if (srt_libspec_static) add_library(${TARGET_srt}_static STATIC ${OBJECT_LIB_SUPPORT} ${VIRTUAL_srt}) + if (WIN32 AND ENABLE_LOCALIF_WIN32) + target_link_libraries(${TARGET_srt}_static PRIVATE Iphlpapi) + add_definitions(-DSRT_ENABLE_LOCALIF_WIN32) + endif() # For Windows, leave the name to be "srt_static.lib". # Windows generates two different library files: @@ -1323,6 +1332,10 @@ macro(srt_add_program_dont_install name) add_executable(${name} ${ARGN}) target_include_directories(${name} PRIVATE apps) target_include_directories(${name} PRIVATE common) + if (WIN32 AND ENABLE_LOCALIF_WIN32) + target_link_libraries(${name} Iphlpapi) + add_definitions(-DSRT_ENABLE_LOCALIF_WIN32) + endif() endmacro() macro(srt_add_program name) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 22c31521b..1efabd927 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -17,6 +17,7 @@ #include #include "srt.h" // Required for SRT_SYNC_CLOCK_* definitions. +#include "common.h" #include "apputil.hpp" #include "netinet_any.h" #include "srt_compat.h" @@ -389,3 +390,40 @@ void PrintLibVersion() const int patch = srtver % 0x100; cerr << "SRT Library version: " << major << "." << minor << "." << patch << ", clock type: " << SRTClockTypeStr() << endl; } + +bool IsTargetAddrSelf(const sockaddr* boundaddr, const sockaddr* targetaddr) +{ + sockaddr_any bound = boundaddr; + sockaddr_any target = targetaddr; + + if (!bound.isany()) + { + // Bound to a specific local address, so only check if + // this isn't the same address as 'target'. + if (target.equal_address(bound)) + { + return true; + } + } + else + { + // Bound to INADDR_ANY, so check matching with any local IP address + const vector& locals = srt::GetLocalInterfaces(); + + // If any of the above function fails, it will collect + // no local interfaces, so it's impossible to check anything. + // OTOH it should also mean that the network isn't working, + // so it's unlikely, as well as no address should match the + // local address anyway. + for (size_t i = 0; i < locals.size(); ++i) + { + if (locals[i].addr.equal_address(target)) + { + return true; + } + } + } + + return false; +} + diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 1a0b158e0..707a816e8 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -336,6 +336,7 @@ std::string OptionHelpItem(const OptionName& o); const char* SRTClockTypeStr(); void PrintLibVersion(); +bool IsTargetAddrSelf(const sockaddr* boundaddr, const sockaddr* targetaddr); namespace srt diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 80e00a774..c0d881d6c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2298,6 +2298,39 @@ void srt::CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_ *pw_namelen = len; } +void srt::CUDTUnited::getsockdevname(const SRTSOCKET u, char* pw_name, size_t* pw_namelen) +{ + if (!pw_name || !pw_namelen) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + CUDTSocket* s = locateSocket(u); + + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + if (s->core().m_bBroken) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + if (s->m_Status == SRTS_INIT) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + const vector& locals = GetLocalInterfaces(); + + for (vector::const_iterator i = locals.begin(); i != locals.end(); ++i) + { + if (i->addr.equal_address(s->m_SelfAddr)) + { + if (*pw_namelen < i->name.size() + 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + memcpy((pw_name), i->name.c_str(), i->name.size()+1); + *pw_namelen = i->name.size(); + return; + } + } + + *pw_namelen = 0; // report empty one +} + 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(); @@ -3903,6 +3936,24 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } +int srt::CUDT::getsockdevname(SRTSOCKET u, char* name, size_t* namelen) +{ + try + { + uglobal().getsockdevname(u, name, namelen); + return 0; + } + catch (const CUDTException& e) + { + return APIError(e); + } + catch (const std::exception& ee) + { + LOGC(aclog.Fatal, log << "getsockname: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); + return APIError(MJ_UNKNOWN, MN_NONE, 0); + } +} + int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) diff --git a/srtcore/api.h b/srtcore/api.h index 4e66d4246..ba69deab0 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -312,6 +312,7 @@ class CUDTUnited int close(CUDTSocket* s); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); + void getsockdevname(const SRTSOCKET u, char* name, size_t* namelen); int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); int selectEx(const std::vector& fds, std::vector* readfds, diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 2e94668ae..696a4ba5a 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -60,6 +60,15 @@ modified by #include #include #include + +#if _WIN32 + #if SRT_ENABLE_LOCALIF_WIN32 + #include + #endif +#else + #include +#endif + #include "udt.h" #include "md5.h" #include "common.h" @@ -511,6 +520,82 @@ ostream& PrintEpollEvent(ostream& os, int events, int et_events) return os; } + +vector GetLocalInterfaces() +{ + vector locals; +#ifdef _WIN32 + // If not enabled, simply an empty local vector will be returned + #if SRT_ENABLE_LOCALIF_WIN32 + ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_ALL_INTERFACES; + ULONG outBufLen = 0; + + // This function doesn't allocate memory by itself, you have to do it + // yourself, worst case when it's too small, the size will be corrected + // and the function will do nothing. So, simply, call the function with + // always too little 0 size and make it show the correct one. + GetAdaptersAddresses(AF_UNSPEC, flags, NULL, NULL, &outBufLen); + // Ignore errors. Check errors on the real call. + // (Have doubts about this "max" here, as VC reports errors when + // using std::max, so it will likely resolve to a macro - hope this + // won't cause portability problems, this code is Windows only. + + // Good, now we can allocate memory + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)::operator new(outBufLen); + ULONG st = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &outBufLen); + if (st == ERROR_SUCCESS) + { + for (PIP_ADAPTER_ADDRESSES i = pAddresses; i; i = pAddresses->Next) + { + std::string name = i->AdapterName; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; + while (pUnicast) + { + LocalInterface a; + if (pUnicast->Address.lpSockaddr) + a.addr = pUnicast->Address.lpSockaddr; + if (a.addr.len > 0) + { + // DO NOT collect addresses that are not of + // AF_INET or AF_INET6 family. + a.name = name; + locals.push_back(a); + } + pUnicast = pUnicast->Next; + } + } + } + + ::operator delete(pAddresses); + #endif + +#else + // Use POSIX method: getifaddrs + struct ifaddrs* pif, * pifa; + int st = getifaddrs(&pifa); + if (st == 0) + { + for (pif = pifa; pif; pif = pif->ifa_next) + { + LocalInterface i; + if (pif->ifa_addr) + i.addr = pif->ifa_addr; + if (i.addr.len > 0) + { + // DO NOT collect addresses that are not of + // AF_INET or AF_INET6 family. + i.name = pif->ifa_name ? pif->ifa_name : ""; + locals.push_back(i); + } + } + } + + freeifaddrs(pifa); +#endif + return locals; +} + + } // namespace srt namespace srt_logging diff --git a/srtcore/common.h b/srtcore/common.h index f58eed4b9..d9dea08d4 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1468,6 +1468,14 @@ inline bool checkMappedIPv4(const sockaddr_in6& sa) std::string FormatLossArray(const std::vector< std::pair >& lra); std::ostream& PrintEpollEvent(std::ostream& os, int events, int et_events = 0); +struct LocalInterface +{ + sockaddr_any addr; + std::string name; +}; + +std::vector GetLocalInterfaces(); + } // namespace srt #endif diff --git a/srtcore/core.h b/srtcore/core.h index ea81646fa..8276ca452 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -217,6 +217,7 @@ class CUDT static int close(SRTSOCKET u); static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); + static int getsockdevname(SRTSOCKET u, char* name, size_t* namelen); static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); static int send(SRTSOCKET u, const char* buf, int len, int flags); diff --git a/srtcore/srt.h b/srtcore/srt.h index 1fa2756b2..9efac1704 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -785,6 +785,7 @@ SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_na SRT_API int srt_close (SRTSOCKET u); SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API int srt_getsockdevname(SRTSOCKET u, char* name, size_t* namelen); SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 8262b8ae0..3c002d4a1 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -159,6 +159,7 @@ int srt_close(SRTSOCKET u) int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } +int srt_getsockdevname(SRTSOCKET u, char* devname, size_t * devnamelen) { return CUDT::getsockdevname(u, devname, devnamelen); } int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) { return CUDT::getsockopt(u, level, optname, optval, optlen); } int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index aedf48f0e..9aeeadc20 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -664,13 +664,17 @@ void SrtCommon::AcceptNewClient() } sockaddr_any agentaddr(AF_INET6); - string agent = ""; + string agent = "", dev = ""; if (-1 != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) { agent = agentaddr.str(); + char name[256]; + size_t len = 255; + if (srt_getsockdevname(m_sock, name, &len) == SRT_SUCCESS) + dev.assign(name, len); } - Verb() << " connected [" << agent << "] <-- " << peer; + Verb() << " connected [" << agent << "] <-- " << peer << " [" << dev << "]"; } ::transmit_throw_on_interrupt = false; @@ -1371,6 +1375,18 @@ static void TransmitConnectCallback(void*, SRTSOCKET socket, int errorcode, cons void SrtCommon::ConnectClient(string host, int port) { auto sa = CreateAddr(host, port); + { + // Check if trying to connect to self. + sockaddr_any lsa; + srt_getsockname(m_sock, lsa.get(), &lsa.len); + + if (lsa.hport() == port && IsTargetAddrSelf(lsa.get(), sa.get())) + { + Verb() << "ERROR: Trying to connect to SELF address " << sa.str() + << " with socket bound to " << lsa.str(); + Error("srt_connect", 0, SRT_EINVPARAM); + } + } Verb() << "Connecting to " << host << ":" << port << " ... " << VerbNoEOL; if (!m_blocking_mode) @@ -1472,6 +1488,18 @@ void SrtCommon::ConnectClient(string host, int port) } Verb() << " connected."; + + sockaddr_any agent; + string dev; + if (Verbose::on) + { + srt_getsockname(m_sock, agent.get(), &agent.len); + char name[256]; + size_t len = 255; + if (srt_getsockdevname(m_sock, name, &len) == SRT_SUCCESS) + dev.assign(name, len); + } + Verb("Connected AGENT:", agent.str(), "[", dev, "] PEER:", sa.str()); stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) Error("ConfigurePost"); From 55216a27d67addee08b3958576ad34ac15d9958f Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:17:15 +0100 Subject: [PATCH 150/174] [core] Restored logger perf improvements that were blocked up to 1.6.0 (#2900) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core] Restored logger perf improvements that were blocked up to 1.6.0 --------- Co-authored-by: Mikołaj Małecki --- srtcore/api.cpp | 5 ++++ srtcore/logging.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++- srtcore/logging.h | 52 +++++++++++------------------------- 3 files changed, 84 insertions(+), 38 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index c0d881d6c..3aea4fe24 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4876,18 +4876,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; + srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); + srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); + srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4895,6 +4898,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); + srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4903,6 +4907,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); + srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d0ba3fd4a..cc9d14e1d 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -14,7 +14,7 @@ written by *****************************************************************************/ -#include "srt_compat.h" + #include "logging.h" using namespace std; @@ -23,6 +23,69 @@ using namespace std; namespace srt_logging { +// Note: subscribe() and unsubscribe() functions are being called +// in the global constructor and destructor only, as the +// Logger objects (and inside them also their LogDispatcher) +// are being created. It's not predicted that LogDispatcher +// object are going to be created any other way than as +// global objects. Therefore the construction and destruction +// of them happens always in the main thread. + +void LogConfig::subscribe(LogDispatcher* lg) +{ + vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); + if (p != loggers.end()) + return; // Do not register twice + + loggers.push_back(lg); +} + +void LogConfig::unsubscribe(LogDispatcher* lg) +{ + vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); + if (p != loggers.end()) + { + loggers.erase(p); + } +} + +// This function doesn't have any protection on itself, +// however the API functions from which it is called, call +// it already under a mutex protection. +void LogConfig::updateLoggersState() +{ + for (vector::iterator p = loggers.begin(); + p != loggers.end(); ++p) + { + (*p)->Update(); + } +} + +void LogDispatcher::Update() +{ + bool enabled_in_fa = src_config->enabled_fa[fa]; + enabled = enabled_in_fa && level <= src_config->max_level; +} + + +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + src_config->log_stream->write(msg.data(), msg.size()); + src_config->log_stream->flush(); + } + src_config->unlock(); +} + #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index 7782245a2..4f6a76005 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,6 +20,7 @@ written by #include #include #include +#include #include #include #ifdef _WIN32 @@ -33,6 +34,7 @@ written by #include "utilities.h" #include "threadname.h" #include "logging_api.h" +#include "srt_compat.h" #include "sync.h" #ifdef __GNUC__ @@ -114,6 +116,7 @@ struct LogConfig void* loghandler_opaque; mutable srt::sync::Mutex mutex; int flags; + std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -136,6 +139,10 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() const { mutex.unlock(); } + + void subscribe(LogDispatcher*); + void unsubscribe(LogDispatcher*); + void updateLoggersState(); }; // The LogDispatcher class represents the object that is responsible for @@ -148,6 +155,7 @@ struct SRT_API LogDispatcher static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; size_t prefix_len; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -158,6 +166,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), + enabled(false), src_config(&config) { const size_t your_pfx_len = your_pfx ? strlen(your_pfx) : 0; @@ -185,13 +194,18 @@ struct SRT_API LogDispatcher prefix[0] = '\0'; prefix_len = 0; } + config.subscribe(this); + Update(); } ~LogDispatcher() { + src_config->unsubscribe(this); } - bool CheckEnabled(); + void Update(); + + bool CheckEnabled() { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -415,24 +429,6 @@ class Logger } }; -inline bool LogDispatcher::CheckEnabled() -{ - // Don't use enabler caching. Check enabled state every time. - - // These assume to be atomically read, so the lock is not needed - // (note that writing to this field is still mutex-protected). - // It's also no problem if the level was changed at the moment - // when the enabler check is tested here. Worst case, the log - // will be printed just a moment after it was turned off. - const LogConfig* config = src_config; // to enforce using const operator[] - config->lock(); - int configured_enabled_fa = config->enabled_fa[fa]; - int configured_maxlevel = config->max_level; - config->unlock(); - - return configured_enabled_fa && level <= configured_maxlevel; -} - #if HAVE_CXX11 @@ -483,24 +479,6 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - src_config->log_stream->write(msg.data(), msg.size()); - (*src_config->log_stream).flush(); - } - src_config->unlock(); -} - } #endif // INC_SRT_LOGGING_H From 3a7b359cb6ac17b284386b8ea6e0aef4ebc4e551 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:23:17 +0100 Subject: [PATCH 151/174] Fixed bug: initial state of an epoll eid for listener socket not set (#2444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added a UT that confirms the bug. Added an option setter utility. * Fixed clang errors. Added compiler flags to configure to prevent changes by google test * Added checking of accept candidates. In-group check disabled and left dead code (this solution deadlocks) * Added tests for more cases and fixed remaining failures. STILL mutex not enabled. * Fixed the problem with locking * Fixed a problem: invalid per-listener report of member connections. Any pending connection can accept a group. Removed other pending sockets after accepting a group. Fixed tests to handle options also with RAII srt initializer * Updated documentation * Added group contents check and configurable sleep for group/listener tests * Added setting UPDATE event on group on new connection. Fixed test with accept timeout * Removed listener field in the group (useless). Improved the test to prove injected connection while reading from group is working --------- Co-authored-by: Mikołaj Małecki --- docs/API/API-functions.md | 134 ++++++++--- docs/API/API-socket-options.md | 5 + srtcore/api.cpp | 226 +++++++++++++----- srtcore/api.h | 8 + srtcore/core.cpp | 21 +- srtcore/epoll.cpp | 27 ++- srtcore/group.cpp | 16 +- srtcore/group.h | 41 +++- test/test_bonding.cpp | 168 +++++++++++++- test/test_epoll.cpp | 405 ++++++++++++++++++++++++++++++++- 10 files changed, 952 insertions(+), 99 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 63da7f651..21a49e393 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -684,41 +684,109 @@ the listener socket to accept group connections SRTSOCKET srt_accept(SRTSOCKET lsn, struct sockaddr* addr, int* addrlen); ``` -Accepts a pending connection, then creates and returns a new socket or -group ID that handles this connection. The group and socket can be -distinguished by checking the `SRTGROUP_MASK` bit on the returned ID. - -* `lsn`: the listener socket previously configured by [`srt_listen`](#srt_listen) -* `addr`: the IP address and port specification for the remote party +Extracts the first connection request on the queue of pending connections for +the listening socket, `lsn`, then creates and returns a new socket or group ID +that handles this connection. The group and socket can be distinguished by +checking the `SRTGROUP_MASK` bit on the returned ID. Note that by default group +connections will be rejected - this feature can be only enabled on demand (see +below). + +* `lsn`: the listening socket +* `addr`: a location to store the remote IP address and port for the connection * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of the returned object -**NOTE:** `addr` is allowed to be NULL, in which case it's understood that the -application is not interested in the address from which the connection originated. -Otherwise `addr` should specify an object into which the address will be written, -and `addrlen` must also specify a variable to contain the object size. Note also -that in the case of group connection only the initial connection that -establishes the group connection is returned, together with its address. As -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`](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`](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 -group, as well as sockets for them, will be created in the background. The -[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) event is raised on the `lsn` socket when -a new background connection is attached to the group, although it's usually for -internal use only. +General requirements for a parameter correctness: + +* `lsn` must be first [bound](#srt_bind) and [listening](#srt_listen) + +* `addr` may be NULL, or otherwise it must be a pointer to an object +that can be treated as an instance of `sockaddr_in` or `sockaddr_in6` + +* `addrlen` should be a pointer to a variable set to the size of the object +specified in `addr`, if `addr` is not NULL. Otherwise it's ignored. + +If `addr` is not NULL, the information about the source IP address and +port of the peer will be written into this object. Note that whichever +type of object is expected here (`sockaddr_in` or `sockaddr_in6`), it +depends on the address type used in the `srt_bind` call for `lsn`. +If unsure in a particular situation, it is recommended that you use +`sockaddr_storage` or `srt::sockaddr_any`. + +If the `lsn` listener socket is in the blocking mode (if +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, +which is default), the call will block until the incoming connection is ready +for extraction. Otherwise, the call always returns immediately, possibly with +failure, if there was no pending connection waiting on the listening socket +`lsn`. + +The listener socket can be checked for any pending connections prior to calling +`srt_accept` by checking the `SRT_EPOLL_ACCEPT` epoll event (which is an alias +to `SRT_EPOLL_IN`). This event might be spurious in certain cases though, for +example, when the connection has been closed by the peer or broken before the +application extracts it. The call to `srt_accept` would then still fail in +such a case. + +In order to allow the listening socket `lsn` to accept a group connection, +the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) socket option +for the listening socket must be set to 1. Note that single socket connections +can still be reported to that socket. The application can distinguish the socket +and group connection by checking the `SRTGROUP_MASK` bit on the returned +successful value. There are some important differences to single socket +connections: + +1. Accepting a group connection can be done only once per connection, even +though particular member connections can get broken or established while +the group is connected. The actual connection reporter (listener) is a socket, +like before, but once you call `srt_accept` and receive this group ID, it is +the group considered connected, and any member connections of the same group +will be handled in the background. + +2. If a group was extracted from the `srt_accept` call, the address reported in +`addr` parameter is still the address of the connection that has triggered the +group connection extraction. The information about all member links in the +group at the moment can be obtained at any time through +[`srt_group_data`](#srt_group_data) or the data filled by +[`srt_sendmsg2`](#srt_sendmsg2) and [`srt_recvmsg2`](#srt_recvmsg2) +in the [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure. + +3. Listening sockets are not bound to groups anyhow. You can allow multiple +listening sockets to accept group connections and the connection extracted +from the listener, if it is declared to be a group member, will join its +group, no matter which of the listening sockets has received the connection +request. This feature is prone to more tricky rules, however: + + * If you use multiple listener sockets, all of them in blocking mode, + allowed for group connections, and receiving connection requests for + the same group at the moment, and you run one thread per `srt_accept` + call, it is undefined, which of them will extract the group ID + for the connection, but still only one will, while the others will + continue blocking. If you want to use only one thread for accepting + connections from potentially multiple listening sockets in the blocking + mode, you should use [`srt_accept_bond`](#srt_accept_bond) instead. + Note though that this function is actually a wrapper that changes locally + to the nonblocking mode on all these listeners and uses epoll internally. + + * If at the moment multiple listener sockets have received connection + request and you query them all for readiness epoll flags (by calling + an epoll waiting function), all of them will get the `SRT_EPOLL_ACCEPT` + flag set, but still only one of them will return the group ID from the + `srt_accept` call. After this call, from all listener sockets in the + whole application the `SRT_EPOLL_ACCEPT` flag, that was set by the reason + of a pending connection for the same group, will be withdrawn (that is, + it will be cleared if there are no other pending connections). This is + then yet another situation when this flag can be spurious. + +4. If you query a listening socket for epoll flags after the `srt_accept` +function has once returned the group ID, the listening sockets that have +received new member connection requests within that group will report only the +[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag. This flag is edge-triggered-only +because there is no operation you can perform in response in order to clear +this flag. This flag is mostly used internally and the application may use it +if it would like to trigger updating the current group information due to +having one newly added member connection. + + | Returns | | |:----------------------------- |:----------------------------------------------------------------------- | @@ -728,7 +796,7 @@ internal use only. | Errors | | |:--------------------------------- |:----------------------------------------------------------------------- | -| [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `addrlen`, when `addr` is not NULL | +| [`SRT_EINVPARAM`](#srt_einvparam) | Invalid `addr` or `addrlen` (see requirements in the begininng) | | [`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 in the non-blocking mode | diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index a02d443a8..094678acc 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -549,6 +549,11 @@ allowed must take this into consideration. It's up to the caller of this function to make this distinction and to take appropriate action depending on the type of entity returned. +Note: this flag should be altered **before** calling `srt_listen`. If you do +this after this call, you might have some pending group connections in the +meantime that will be rejected because group connections are not **yet** +allowed on this listener socket. + When this flag is set to 1 on an accepted socket that is passed to the listener callback handler, it means that this socket is created for a group connection and it will become a member of a group. Note that in this case diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 3aea4fe24..910ff6082 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -744,21 +744,14 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, goto ERR_ROLLBACK; } - // 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 (CUDTGroup::gli_t gi = g->m_Group.begin(); gi != g->m_Group.end(); ++gi) - { - if (gi->laststatus == SRTS_CONNECTED) - { - HLOGC(cnlog.Debug, - log << "Found another connected socket in the group: $" << gi->id - << " - socket will be NOT given up for accepting"); - should_submit_to_accept = false; - break; - } - } + // Acceptance of the group will have to be done through accepting + // of one of the pending sockets. There can be, however, multiple + // such sockets at a time, some of them might get broken before + // being accepted, and therefore we need to make all sockets ready. + // But then, acceptance of a group may happen only once, so if any + // sockets of the same group were submitted to accept, they must + // be removed from the accept queue at this time. + should_submit_to_accept = g->groupPending(); // Update the status in the group so that the next // operation can include the socket in the group operation. @@ -771,34 +764,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, gm->rcvstate = SRT_GST_IDLE; gm->laststatus = SRTS_CONNECTED; - if (!g->m_bConnected) - { - HLOGC(cnlog.Debug, log << "newConnection(GROUP): First socket connected, SETTING GROUP CONNECTED"); - g->m_bConnected = true; - } + g->setGroupConnected(); - // XXX PROLBEM!!! These events are subscribed here so that this is done once, lazily, - // but groupwise connections could be accepted from multiple listeners for the same group! - // m_listener MUST BE A CONTAINER, NOT POINTER!!! - // ALSO: Maybe checking "the same listener" is not necessary as subscruption may be done - // multiple times anyway? - if (!g->m_listener) - { - // Newly created group from the listener, which hasn't yet - // the listener set. - g->m_listener = ls; - - // Listen on both first connected socket and continued sockets. - // This might help with jump-over situations, and in regular continued - // sockets the IN event won't be reported anyway. - int listener_modes = SRT_EPOLL_ACCEPT | SRT_EPOLL_UPDATE; - epoll_add_usock_INTERNAL(g->m_RcvEID, ls, &listener_modes); - - // This listening should be done always when a first connected socket - // appears as accepted off the listener. This is for the sake of swait() calls - // inside the group receiving and sending functions so that they get - // interrupted when a new socket is connected. - } // Add also per-direction subscription for the about-to-be-accepted socket. // Both first accepted socket that makes the group-accept and every next @@ -860,6 +827,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // 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->core().m_sPollID, SRT_EPOLL_UPDATE, true); +#if ENABLE_BONDING + // Note that the code in this current IF branch can only be executed in case + // of group members. Otherwise should_submit_to_accept will be always true. + if (ns->m_GroupOf) + { + HLOGC(gmlog.Debug, log << "GROUP UPDATE $" << ns->m_GroupOf->id() << " per connected socket @" << ns->m_SocketID); + m_EPoll.update_events(ns->m_GroupOf->id(), ns->m_GroupOf->m_sPollID, SRT_EPOLL_UPDATE, true); + } +#endif CGlobEvent::triggerEvent(); } @@ -906,6 +882,68 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return 1; } +SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() +{ + // You need to check EVERY socket that has been queued + // and verify its internals. With independent socket the + // matter is simple - if it's present, you light up the + // SRT_EPOLL_ACCEPT flag. + +#if !ENABLE_BONDING + ScopedLock accept_lock (m_AcceptLock); + + // Make it simplified here - nonempty container = have acceptable sockets. + // Might make sometimes spurious acceptance, but this can also happen when + // the incoming accepted socket was suddenly broken. + return m_QueuedSockets.empty() ? 0 : int(SRT_EPOLL_ACCEPT); + +#else // Could do #endif here, but the compiler would complain about unreachable code. + + map sockets_copy; + { + ScopedLock accept_lock (m_AcceptLock); + sockets_copy = m_QueuedSockets; + } + return CUDT::uglobal().checkQueuedSocketsEvents(sockets_copy); + +#endif +} + +#if ENABLE_BONDING +int srt::CUDTUnited::checkQueuedSocketsEvents(const map& sockets) +{ + SRT_EPOLL_T flags = 0; + + // But with the member sockets an appropriate check must be + // done first: if this socket belongs to a group that is + // already in the connected state, you should light up the + // SRT_EPOLL_UPDATE flag instead. This flag is only for + // internal informing the waiters on the listening sockets + // that they should re-read the group list and re-check readiness. + + // Now we can do lock once and for all + for (map::const_iterator i = sockets.begin(); i != sockets.end(); ++i) + { + CUDTSocket* s = locateSocket_LOCKED(i->first); + if (!s) + continue; // wiped in the meantime - ignore + + // If this pending socket is a group member, but the group + // to which it belongs is NOT waiting to be accepted, then + // light up the UPDATE event only. Light up ACCEPT only if + // this is a single socket, or this single socket has turned + // the mirror group to be first time available for accept(), + // and this accept() hasn't been done yet. + if (s->m_GroupOf && !s->m_GroupOf->groupPending()) + flags |= SRT_EPOLL_UPDATE; + else + flags |= SRT_EPOLL_ACCEPT; + } + + return flags; +} +#endif + // static forwarder int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { @@ -1239,12 +1277,16 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int // it's a theoretically possible scenario if (s->m_GroupOf) { - u = s->m_GroupOf->m_GroupID; - s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure - + CUDTGroup* g = s->m_GroupOf; // 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(); + g->m_stats.tsLastSampleTime = steady_clock::now(); + + HLOGC(cnlog.Debug, log << "accept: reporting group $" << g->m_GroupID << " instead of member socket @" << u); + u = g->m_GroupID; + s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure + g->m_bPending = false; + CUDT::uglobal().removePendingForGroup(g); } else { @@ -1264,6 +1306,79 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int return u; } +#if ENABLE_BONDING + +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::removePendingForGroup(const CUDTGroup* g) +{ + // We don't have a list of listener sockets that have ever + // reported a pending connection for a group, so the only + // way to find them is to ride over the list of all sockets... + + list members; + g->getMemberSockets((members)); + + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++i) + { + CUDTSocket* s = i->second; + // Check if any of them is a listener socket... + + /* XXX This is left for information only that we are only + interested with listener sockets - with the current + implementation checking it is pointless because the + m_QueuedSockets structure is present in every socket + anyway even if it's not a listener, and only listener + sockets may have this container nonempty. So checking + the container should suffice. + + if (!s->core().m_bListening) + continue; + */ + + if (s->m_QueuedSockets.empty()) + continue; + + // Somehow fortunate for us that it's a set, so we + // can simply check if this allegedly listener socket + // contains any of them. + for (list::iterator m = members.begin(), mx = m; m != members.end(); m = mx) + { + ++mx; + std::map::iterator q = s->m_QueuedSockets.find(*m); + if (q != s->m_QueuedSockets.end()) + { + HLOGC(cnlog.Debug, log << "accept: listener @" << s->m_SocketID + << " had ququed member @" << *m << " -- removed"); + // Found an intersection socket. + // Remove it from the listener queue + s->m_QueuedSockets.erase(q); + + // NOTE ALSO that after this removal the queue may be EMPTY, + // and if so, the listener socket should be no longer ready for accept. + if (s->m_QueuedSockets.empty()) + { + m_EPoll.update_events(s->m_SocketID, s->core().m_sPollID, SRT_EPOLL_ACCEPT, false); + } + + // and remove it also from the members list. + // This can be done safely because we use a SAFE LOOP. + // We can also do it safely because a socket may be + // present in only one listener socket in the whole app. + members.erase(m); + } + } + + // It may happen that the list of members can be + // eventually purged even if we haven't checked every socket. + // If it happens so, quit immediately because there's nothing + // left to do. + if (members.empty()) + return; + } +} + +#endif + int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified @@ -2524,14 +2639,19 @@ int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int } #endif - CUDTSocket* s = locateSocket(u); - if (s) + // The call to epoll_add_usock_INTERNAL is expected + // to be called under m_GlobControlLock, so use this lock here, too. { - ret = epoll_add_usock_INTERNAL(eid, s, events); - } - else - { - throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + ScopedLock cs (m_GlobControlLock); + CUDTSocket* s = locateSocket_LOCKED(u); + if (s) + { + ret = epoll_add_usock_INTERNAL(eid, s, events); + } + else + { + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + } } return ret; diff --git a/srtcore/api.h b/srtcore/api.h index ba69deab0..a69b4b618 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -171,6 +171,8 @@ class CUDTSocket unsigned int m_uiBackLog; //< maximum number of connections in queue + SRT_EPOLL_T getListenerEvents(); + // XXX A refactoring might be needed here. // There are no reasons found why the socket can't contain a list iterator to a @@ -286,6 +288,12 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); +#if ENABLE_BONDING + SRT_ATTR_REQUIRES(m_GlobControlLock) + int checkQueuedSocketsEvents(const std::map& sockets); + void removePendingForGroup(const CUDTGroup* g); +#endif + 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 75f0c04b5..590f4b756 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3358,7 +3358,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // This can only happen on a listener (it's only called on a site that is // HSD_RESPONDER), so it was a response for a groupwise connection. // Therefore such a group shall always be considered opened. - gp->setOpen(); + // It's also set pending and it stays this way until accepted. + gp->setOpenPending(); HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $" @@ -11872,6 +11873,24 @@ void srt::CUDT::addEPoll(const int eid) m_sPollID.insert(eid); leaveCS(uglobal().m_EPoll.m_EPollLock); + if (m_bListening) + { + // A listener socket can only get readiness on SRT_EPOLL_ACCEPT + // (which has the same value as SRT_EPOLL_IN), or sometimes + // also SRT_EPOLL_UPDATE. All interesting fields for that purpose + // are contained in the CUDTSocket class, so redirect there. + SRT_EPOLL_T events = m_parent->getListenerEvents(); + + // Only light up the events that were returned, do nothing if none is ready, + // the "no event" state is the default. + if (events) + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, events, true); + + // You don't check anything else here - a listener socket can be only + // used for listening and nothing else. + return; + } + if (!stillConnected()) return; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 8cd8440c7..2c891f96b 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -500,18 +500,23 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) + { + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " doesn't exist"); throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + } CEPollDesc& ed = p->second; if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty()) { // Empty EID is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is empty (use SRT_EPOLL_ENABLE_EMPTY to allow)"); throw CUDTException(MJ_NOTSUP, MN_EEMPTY); } if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0)) { - // Empty EID is not allowed, report error. + // Empty container is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: empty output container with E" << eid << " (use SRT_EPOLL_ENABLE_OUTPUTCHECK to allow)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -519,6 +524,7 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 { // XXX Add error log // uwait should not be used with EIDs subscribed to system sockets + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is subscribed to system sckets (not allowed for uwait)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -530,11 +536,20 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ++total; if (total > fdsSize) + { + HLOGC(ealog.Debug, log << "epoll_uwait: output container size=" << fdsSize << " insufficient to report all sockets"); break; + } fdsSet[pos] = *i; + IF_HEAVY_LOGGING(std::ostringstream out); + IF_HEAVY_LOGGING(out << "epoll_uwait: Notice: fd=" << i->fd << " events="); + IF_HEAVY_LOGGING(PrintEpollEvent(out, i->events, 0)); + + SRT_ATR_UNUSED const bool was_edge = ed.checkEdge(i++); // NOTE: potentially deletes `i` + IF_HEAVY_LOGGING(out << (was_edge ? "(^)" : "")); + HLOGP(ealog.Debug, out.str()); - ed.checkEdge(i++); // NOTE: potentially deletes `i` } if (total) return total; @@ -810,7 +825,7 @@ int srt::CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut // Logging into 'singles' because it notifies as to whether // the edge-triggered event has been cleared - HLOGC(ealog.Debug, log << "E" << d.m_iID << " rdy=" << total << ": " + HLOGC(ealog.Debug, log << "swait: E" << d.m_iID << " rdy=" << total << ": " << singles.str() << " TRACKED: " << d.DisplayEpollWatch()); return total; @@ -869,6 +884,12 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return -1; // still, ignored. } + if (uid == SRT_INVALID_SOCK || uid == 0) // XXX change to a symbolic value + { + LOGC(eilog.Fatal, log << "epoll/update: IPE: invalid 'uid' submitted for update!"); + return -1; + } + int nupdated = 0; vector lost; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 6a868aaf9..275d0c376 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -252,7 +252,6 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_GroupID(-1) , m_PeerGroupID(-1) , m_type(gtype) - , m_listener() , m_iBusy() , m_iSndOldestMsgNo(SRT_MSGNO_NONE) , m_iSndAckedMsgNo(SRT_MSGNO_NONE) @@ -274,6 +273,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_RcvBaseSeqNo(SRT_SEQNO_NONE) , m_bOpened(false) , m_bConnected(false) + , m_bPending(false) , m_bClosing(false) , m_iLastSchedSeqNo(SRT_SEQNO_NONE) , m_iLastSchedMsgNo(SRT_MSGNO_NONE) @@ -284,6 +284,8 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) m_RcvEID = m_Global.m_EPoll.create(&m_RcvEpolld); m_SndEID = m_Global.m_EPoll.create(&m_SndEpolld); + HLOGC(gmlog.Debug, log << "Group internal EID: R:E" << m_RcvEID << " W:E" << m_SndEID); + m_stats.init(); // Set this data immediately during creation before @@ -2196,6 +2198,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // This call may wait indefinite time, so GroupLock must be unlocked. InvertedLock ung (m_GroupLock); THREAD_PAUSED(); + HLOGC(grlog.Debug, log << "group/recv: e-polling E" << m_RcvEID << " timeout=" << timeout << "ms"); nready = m_Global.m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); THREAD_RESUMED(); @@ -4213,6 +4216,7 @@ void CUDTGroup::setGroupConnected() { if (!m_bConnected) { + HLOGC(cnlog.Debug, log << "GROUP: First socket connected, SETTING GROUP CONNECTED"); // Switch to connected state and give appropriate signal m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); m_bConnected = true; @@ -4285,6 +4289,16 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } } +void CUDTGroup::getMemberSockets(std::list& w_ids) const +{ + ScopedLock gl (m_GroupLock); + + for (cgli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + w_ids.push_back(gi->id); + } +} + void CUDTGroup::activateUpdateEvent(bool still_have_items) { // This function actually reacts on the fact that a socket diff --git a/srtcore/group.h b/srtcore/group.h index 56f1456f8..6fd5c5913 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -101,6 +101,7 @@ class CUDTGroup typedef std::list group_t; typedef group_t::iterator gli_t; + typedef group_t::const_iterator cgli_t; typedef std::vector< std::pair > sendable_t; struct Sendstate @@ -203,6 +204,11 @@ class CUDTGroup return m_Group.empty(); } + bool groupPending() + { + return m_bPending; + } + void setGroupConnected(); int send(const char* buf, int len, SRT_MSGCTRL& w_mc); @@ -399,7 +405,7 @@ class CUDTGroup void getGroupCount(size_t& w_size, bool& w_still_alive); srt::CUDTUnited& m_Global; - srt::sync::Mutex m_GroupLock; + mutable srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; SRTSOCKET m_PeerGroupID; @@ -428,6 +434,8 @@ class CUDTGroup gli_t begin() { return m_List.begin(); } gli_t end() { return m_List.end(); } + cgli_t begin() const { return m_List.begin(); } + cgli_t end() const { return m_List.end(); } bool empty() { return m_List.empty(); } void push_back(const SocketData& data) { m_List.push_back(data); ++m_SizeCache; } void clear() @@ -442,7 +450,6 @@ class CUDTGroup }; GroupContainer m_Group; SRT_GROUP_TYPE m_type; - CUDTSocket* m_listener; // A "group" can only have one listener. srt::sync::atomic m_iBusy; CallbackHolder m_cbConnectHook; void installConnectHook(srt_connect_callback_fn* hook, void* opaq) @@ -661,8 +668,21 @@ class CUDTGroup // from the first delivering socket will be taken as a good deal. sync::atomic m_RcvBaseSeqNo; - bool m_bOpened; // Set to true when at least one link is at least pending - bool m_bConnected; // Set to true on first link confirmed connected + /// True: at least one socket has joined the group in at least pending state + bool m_bOpened; + + /// True: at least one socket is connected, even if pending from the listener + bool m_bConnected; + + /// True: this group was created on the listner side for the first socket + /// that is pending connection, so the group is about to be reported for the + /// srt_accept() call, but the application hasn't retrieved the group yet. + /// Not in use in case of caller-side groups. + // NOTE: using atomic in otder to allow this variable to be changed independently + // on any mutex locks. + sync::atomic m_bPending; + + /// True: the group was requested to close and it should not allow any operations. bool m_bClosing; // There's no simple way of transforming config @@ -736,13 +756,20 @@ class CUDTGroup // Required after the call on newGroup on the listener side. // On the listener side the group is lazily created just before // accepting a new socket and therefore always open. - void setOpen() { m_bOpened = true; } + // However, after creation it will be still waiting for being + // extracted by the application in `srt_accept`, and until then + // it stays as pending. + void setOpenPending() + { + m_bOpened = true; + m_bPending = true; + } std::string CONID() const { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_GroupID << ":"; + os << "$" << m_GroupID << ":"; return os.str(); #else return ""; @@ -796,6 +823,8 @@ class CUDTGroup void updateLatestRcv(srt::CUDTSocket*); + void getMemberSockets(std::list&) const; + // Property accessors SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID); SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, peerid, m_PeerGroupID); diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index a1df9ec2d..55ab26270 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -5,9 +5,10 @@ #include #include "gtest/gtest.h" #include "test_env.h" +#include "apputil.hpp" // Note: declares CreateAddr, but not srt::CreateAddr #include "srt.h" -#include "udt.h" +#include "logging_api.h" #include "common.h" #include "netinet_any.h" #include "socketconfig.h" @@ -637,6 +638,171 @@ TEST(Bonding, InitialFailure) srt_close(lsn); } +void SetLongSilenceTolerant(const SRTSOCKET s) +{ + int longtime = 100000; + + srt_setsockflag(s, SRTO_CONNTIMEO, &longtime, sizeof longtime); + srt_setsockflag(s, SRTO_PEERIDLETIMEO, &longtime, sizeof longtime); +} + +TEST(Bonding, DeadLinkUpdate) +{ + using namespace std; + using namespace std::chrono; + + srt::TestInit srtinit; + + SRTSOCKET listener = srt_create_socket(); + const SRTSOCKET group = srt_create_group(SRT_GTYPE_BACKUP); + + SetLongSilenceTolerant(listener); + SetLongSilenceTolerant(group); + + srt::sockaddr_any sa(AF_INET); + + inet_pton(AF_INET, "127.0.0.1", sa.get_addr()); + + sa.hport(5555); + + srt_bind(listener, sa.get(), sa.size()); + srt::setopt(listener)[SRTO_GROUPCONNECT] = 1; + srt_listen(listener, 1); + char srcbuf [] = "1234ABCD"; + + thread td = thread([&]() { + srt::ThreadName::set("TEST-conn"); + + cout << "[T] Connecting 1...\n"; + const SRTSOCKET member1 = srt_connect(group, sa.get(), sa.size()); + EXPECT_NE(member1, SRT_INVALID_SOCK); + + int nsent = srt_send(group, srcbuf, sizeof srcbuf); + // Now wait 3s + cout << "[T] Link 1 established. Wait 3s...\n"; + this_thread::sleep_for(seconds(3)); + + cout << "[T] Connecting 2...\n"; + // Make a second connection + + SRT_SOCKGROUPCONFIG sco [] = { + srt_prepare_endpoint(NULL, sa.get(), sa.size()) + }; + sco[0].weight = 10; + + const SRTSOCKET member2 = srt_connect_group(group, sco, 1); + EXPECT_NE(member2, SRT_INVALID_SOCK); + + if (member2 == SRT_INVALID_SOCK || member1 == SRT_INVALID_SOCK) + { + srt_close(member1); + srt_close(member2); + cout << "[T] Test already failed, exitting\n"; + return; + } + + cout << "[T] Link 2 established. Wait 3s...\n"; + // Again wait 3s + this_thread::sleep_for(seconds(3)); + + // DO NOT kill link 1 because this will send the shutdown + // signal and this way force update on the old socket. We + // need a silence in order to check if forever waiting on + // swait for group reading will be updated with the newly + // connected socket. Leaving the code for a case when some + // manual development testing is required. + //cout << "[T] Killing link 1...\n"; + // Now close the first connection + //srt_close(member1); + + // Now send the data and see if they are received + cout << "[T] Sending: size=" << (sizeof srcbuf) << " Content: '" << srcbuf << "'...\n"; + nsent = srt_send(group, srcbuf, sizeof srcbuf); + EXPECT_NE(nsent, -1) << "srt_send:" << srt_getlasterror_str(); + + cout << "[T] Wait 3s...\n"; + // Again wait 3s + this_thread::sleep_for(seconds(3)); + + cout << "[T] Killing the group and exitting.\n"; + // And close + srt_close(group); + cout << "[T] exit\n"; + }); + + struct AtReturnJoin + { + thread& thr; + ~AtReturnJoin() + { + thr.join(); + } + } atreturn_join { td }; + + cout << "Accepting (10s timeout)...\n"; + // Using srt_accept_bond to apply accept timeout + SRTSOCKET lsnra [] = { listener }; + const SRTSOCKET acp = srt_accept_bond(lsnra, 1, 10*1000); + + EXPECT_NE(acp, -1) << "srt_accept:" << srt_getlasterror_str(); + EXPECT_EQ(acp & SRTGROUP_MASK, SRTGROUP_MASK); + + cout << "Accepted, closing listener...\n"; + + // Close and set up the listener again. + srt_close(listener); + if (acp == SRT_INVALID_SOCK) + return; + + cout << "Creating new listener...\n"; + listener = srt_create_socket(); + srt_bind(listener, sa.get(), sa.size()); + srt::setopt(listener)[SRTO_GROUPCONNECT] = 1; + srt_listen(listener, 1); + + cout << "Group accepted. Receiving...\n"; + char buf[1316] = ""; + const int nrecv = srt_recv(acp, buf, 1316); + int syserr, err; + err = srt_getlasterror(&syserr); + EXPECT_NE(nrecv, -1) << "srt_recv:" << srt_getlasterror_str(); + + cout << "Received: val=" << nrecv << " Content: '" << buf << "'\n"; + if (nrecv == -1) + { + cout << "ERROR: " << srt_strerror(err, syserr) << endl; + cout << "STATUS: " << srt_logging::SockStatusStr(srt_getsockstate(acp)) << endl; + } + else + { + EXPECT_EQ(strcmp(srcbuf, buf), 0); + } + + cout << "Receiving again...\n"; + // Now receive again, the second portion. + const int nrecv2 = srt_recv(acp, buf, 1316); + err = srt_getlasterror(&syserr); + EXPECT_NE(nrecv, -1) << "srt_recv:" << srt_getlasterror_str(); + + cout << "Received: val=" << nrecv2 << " Content: '" << buf << "'\n"; + if (nrecv2 == -1) + { + cout << "ERROR: " << srt_strerror(err, syserr) << endl; + cout << "STATUS: " << srt_logging::SockStatusStr(srt_getsockstate(acp)) << endl; + } + else + { + EXPECT_EQ(strcmp(srcbuf, buf), 0); + } + + cout << "Closing.\n"; + srt_close(acp); + srt_close(listener); + + ASSERT_NE(nrecv, -1); + + EXPECT_EQ(strcmp(srcbuf, buf), 0); +} // General idea: diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index f9d838030..742c37325 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -7,7 +7,7 @@ #include "test_env.h" #include "api.h" #include "epoll.h" - +#include "apputil.hpp" using namespace std; using namespace srt; @@ -545,6 +545,409 @@ TEST(CEPoll, ThreadedUpdate) } } +void testListenerReady(const bool LATE_CALL, size_t nmembers) +{ + bool is_single = true; + bool want_sleep = !TestEnv::me->OptionPresent("nosleep"); + + 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); + + TestInit init; + + SRTSOCKET server_sock, caller_sock; + server_sock = srt_create_socket(); + + if (nmembers > 0) + { + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + is_single = false; + } + else + { + caller_sock = srt_create_socket(); + nmembers = 1; // Set to 1 so that caller starts at least once. + } + + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, nmembers+1); + + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; + + std::vector> connect_res; + + if (LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } + + std::cout << "STARTED connecting...\n"; + } + + if (want_sleep) + { + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + } + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); + + if (!LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } + + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Waiting for readiness...\n"; + // And see now if the waiting accepted socket reports it. + SRT_EPOLL_EVENT fdset[1]; + EXPECT_EQ(srt_epoll_uwait(eid, fdset, 1, 5000), 1); + + std::cout << "Accepting...\n"; + sockaddr_in scl; + int sclen = sizeof scl; + SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + if (nmembers > 1) + { + std::cout << "With >1 members, check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + std::cout << "[T:X] CALLING (expected failure)\n"; + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + EXPECT_EQ(srt_epoll_uwait(eid_postcheck, fdset, 1, 5000), 1); + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(SRT_EPOLL_OPT(fdset[0].events), SRT_EPOLL_UPDATE); + SRTSOCKET joined = extra_call.get(); + EXPECT_NE(joined, SRT_INVALID_SOCK); + std::cout << Sprint("Extra joined: @", joined, "\n"); + } + + std::vector gdata; + + if (!is_single) + { + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Listener) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); + } + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < nmembers; ++i) + { + std::cout << "Join: #" << i << ":\n"; + SRTSOCKET called_socket = connect_res[i].get(); + std::cout << "... " << called_socket << std::endl; + EXPECT_NE(called_socket, SRT_INVALID_SOCK); + } + + if (!is_single) + { + EXPECT_EQ(caller_sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(caller_sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Caller) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); + + if (want_sleep) + { + std::cout << "Sleep for 3 seconds to avoid closing-in-between\n"; + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + } + + std::cout << "Releasing EID resources and all sockets\n"; + + srt_epoll_release(eid); + srt_epoll_release(eid_postcheck); + + srt_close(server_sock); + srt_close(caller_sock); + srt_close(sock); +} + +TEST(CEPoll, EarlyListenerReady) +{ + testListenerReady(false, 0); +} + +TEST(CEPoll, LateListenerReady) +{ + testListenerReady(true, 0); +} + +#if ENABLE_BONDING + +TEST(CEPoll, EarlyGroupListenerReady_1) +{ + testListenerReady(false, 1); +} + +TEST(CEPoll, LateGroupListenerReady_1) +{ + testListenerReady(true, 1); +} + +TEST(CEPoll, EarlyGroupListenerReady_3) +{ + testListenerReady(false, 3); +} + +TEST(CEPoll, LateGroupListenerReady_3) +{ + testListenerReady(true, 3); +} + + +void testMultipleListenerReady(const bool LATE_CALL) +{ + 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); + + sockaddr_in sa2; + memset(&sa2, 0, sizeof sa2); + sa2.sin_family = AF_INET; + sa2.sin_port = htons(5556); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa2.sin_addr), 1); + + TestInit init; + + SRTSOCKET server_sock, server_sock2, caller_sock; + server_sock = srt_create_socket(); + server_sock2 = srt_create_socket(); + + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + EXPECT_NE(srt_setsockflag(server_sock2, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, 3); + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + srt_bind(server_sock2, (sockaddr*)& sa2, sizeof(sa2)); + srt_listen(server_sock2, 3); + srt::setopt(server_sock2)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; + + std::vector> connect_res; + + if (LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); + })); + + + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid, server_sock2, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock2, &modes_postcheck), SRT_ERROR); + + if (!LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); + })); + + std::cout << "STARTED connecting...\n"; + } + + // Sleep to make sure that the connection process has started. + this_thread::sleep_for(chrono::milliseconds(100)); + + std::cout << "Waiting for readiness on @" << server_sock << " and @" << server_sock2 << "\n"; + // And see now if the waiting accepted socket reports it. + + // This time we should expect that the connection reports in + // on two listener sockets + SRT_EPOLL_EVENT fdset[2] = {}; + std::ostringstream out; + + int nready = srt_epoll_uwait(eid, fdset, 2, 5000); + EXPECT_EQ(nready, 2); + out << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + out << " @" << fdset[i].fd; + PrintEpollEvent(out, fdset[i].events); + } + out << std::endl; + std::cout << out.str(); + + std::cout << "Accepting...\n"; + sockaddr_in scl; + int sclen = sizeof scl; + + // We choose the SECOND one to extract the group connection. + SRTSOCKET sock = srt_accept(server_sock2, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + // Make sure this time that the accepted connection is a group. + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + + std::cout << "Check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + // Note that if this was done AFTER accepting, the UPDATE would + // be only set one one socket. + nready = srt_epoll_uwait(eid_postcheck, fdset, 1, 5000); + EXPECT_EQ(nready, 1); + + std::cout << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + std::cout << " @" << fdset[i].fd; + PrintEpollEvent(std::cout, fdset[i].events); + } + std::cout << std::endl; + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(SRT_EPOLL_OPT(fdset[0].events), SRT_EPOLL_UPDATE); + EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < connect_res.size(); ++i) + { + EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + } + + srt_epoll_release(eid); + srt_epoll_release(eid_postcheck); + + srt_close(server_sock); + srt_close(server_sock2); + srt_close(caller_sock); + srt_close(sock); +} + +TEST(CEPoll, EarlyGroupMultiListenerReady) +{ + testMultipleListenerReady(false); +} + +TEST(CEPoll, LateGroupMultiListenerReady) +{ + testMultipleListenerReady(true); +} + + + +#endif + class TestEPoll: public srt::Test { From 75f56d9a87039c0598d434e67cdcefd3260fb6f3 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 19 Mar 2025 18:24:20 +0800 Subject: [PATCH 152/174] [core] Fixed overrideSndSeqNo() not clear buffer. (#2753) Co-authored-by: Sektor van Skijlen --- srtcore/buffer_snd.cpp | 12 ++++++++++++ srtcore/buffer_snd.h | 1 + srtcore/core.cpp | 12 ++++++++++-- srtcore/group.cpp | 27 +++++++++++++++------------ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index ef1bc498c..9661048b0 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -682,6 +682,18 @@ int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_ return (dpkts); } +int CSndBuffer::dropAll(int& w_bytes) +{ + ScopedLock bufferguard(m_BufLock); + const int dpkts = m_iCount; + w_bytes = m_iBytesCount; + m_pFirstBlock = m_pCurrBlock = m_pLastBlock; + m_iCount = 0; + m_iBytesCount = 0; + updAvgBufSize(steady_clock::now()); + return dpkts; +} + void CSndBuffer::increase() { int unitsize = m_pBuffer->m_iSize; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index afd52110b..9e47d483e 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -177,6 +177,7 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + int dropAll(int& bytes); void updAvgBufSize(const time_point& time); int getAvgBufSize(int& bytes, int& timespan); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 590f4b756..de7723612 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10117,7 +10117,15 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return false; } - // + int dbytes; + const int dpkts SRT_ATR_UNUSED = m_pSndBuffer->dropAll((dbytes)); + + enterCS(m_StatsLock); + m_stats.sndr.dropped.count(dbytes);; + leaveCS(m_StatsLock); + + m_pSndLossList->removeUpTo(CSeqNo::decseq(seq)); + // The peer will have to do the same, as a reaction on perceived // packet loss. When it recognizes that this initial screwing up // has happened, it should simply ignore the loss and go on. @@ -10131,7 +10139,7 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) HLOGC(gslog.Debug, log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo - << " (unchanged)"); + << " (unchanged) dropped-pkts=" << dpkts); return true; } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 275d0c376..9fed5e539 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4033,7 +4033,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // This should resend all packets if (m_SenderBuffer.empty()) { - LOGC(gslog.Fatal, log << "IPE: sendBackupRexmit: sender buffer empty"); + LOGC(gslog.Fatal, log << core.CONID() << "IPE: sendBackupRexmit: sender buffer empty"); // Although act as if it was successful, otherwise you'll get connection break return 0; @@ -4065,8 +4065,9 @@ 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. Link seqno %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq - << " - DENIED; skip " << skip_initial << " pkts, " << m_SenderBuffer.size() << " pkts in buffer"); + log << core.CONID() << "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 { @@ -4075,11 +4076,11 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // sequence with it first so that they go hand-in-hand with // 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 SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); - HLOGC(gslog.Debug, - log << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq - << (su ? " - succeeded" : " - FAILED!")); + const int32_t old SRT_ATR_UNUSED = core.schedSeqNo(); + const bool success SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); + LOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq + << (success ? " - succeeded" : " - FAILED!")); } } @@ -4087,8 +4088,8 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) 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"); + log << core.CONID() << "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 } @@ -4104,14 +4105,16 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { // Stop sending if one sending ended up with error LOGC(gslog.Warn, - log << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() << " and FAILED"); + log << core.CONID() << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() + << " and FAILED"); return -1; } } // Copy the contents of the last item being updated. w_mc = m_SenderBuffer.back().mc; - HLOGC(gslog.Debug, log << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); + HLOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); return stat; } From b397bc64f44e2e669f28805b6da3d36a2a3b082b Mon Sep 17 00:00:00 2001 From: Nicolas Panhaleux <135953503+npanhaleux@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:43:00 +0100 Subject: [PATCH 153/174] [core] New moving average value for sent/recv rates over last second (#3009) * [core] Average values for sent/recv bitrate are now over last second. * [core] Adding packet header size in average measure * [core] Removing inheritance and fixing issue in rate compute --- srtcore/buffer_tools.cpp | 60 +++++++++++++++++++++++++++++ srtcore/buffer_tools.h | 81 +++++++++++++++++++++++++++++++++++++++- srtcore/core.cpp | 13 +++++-- srtcore/stats.h | 17 +++++++++ 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 054231915..471fc0b5c 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -273,5 +273,65 @@ int CSndRateEstimator::incSampleIdx(int val, int inc) const return val; } +CMovingRateEstimator::CMovingRateEstimator() + : m_tsFirstSampleTime(sync::steady_clock::now()) + , m_iCurSampleIdx(0) + , m_iRateBps(0) + , m_Samples(NUM_PERIODS) +{ + resetRate(0, NUM_PERIODS); } +void CMovingRateEstimator::addSample(int pkts, double bytes) +{ + const time_point now = steady_clock::now(); + const int iSampleDeltaIdx = int(count_milliseconds(now - m_tsLastSlotTimestamp) / SAMPLE_DURATION_MS); + + if (iSampleDeltaIdx == 0) + { + m_Samples[m_iCurSampleIdx].m_iBytesCount += bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount += pkts; + } + else + { + if ((m_iCurSampleIdx + iSampleDeltaIdx) < NUM_PERIODS) + resetRate(m_iCurSampleIdx + 1, m_iCurSampleIdx + iSampleDeltaIdx); + else + { + int loopbackDiff = m_iCurSampleIdx + iSampleDeltaIdx - NUM_PERIODS; + resetRate(m_iCurSampleIdx + 1, NUM_PERIODS); + resetRate(0, loopbackDiff); + } + + m_iCurSampleIdx = ((m_iCurSampleIdx + iSampleDeltaIdx) % NUM_PERIODS); + m_Samples[m_iCurSampleIdx].m_iBytesCount = bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount = pkts; + + m_tsLastSlotTimestamp += milliseconds_from(SAMPLE_DURATION_MS * iSampleDeltaIdx); + + computeAverageValue(); + } +} + +void CMovingRateEstimator::resetRate(int from, int to) +{ + for (int i = max(0, from); i < min(int(NUM_PERIODS), to); i++) + m_Samples[i].reset(); +} + +void CMovingRateEstimator::computeAverageValue() +{ + const time_point now = steady_clock::now(); + const int startDelta = count_milliseconds(now - m_tsFirstSampleTime); + const bool isFirstPeriod = startDelta < (SAMPLE_DURATION_MS * NUM_PERIODS); + int newRateBps = 0; + + for (int i = 0; i < NUM_PERIODS; i++) + newRateBps += (m_Samples[i].m_iBytesCount + (CPacket::HDR_SIZE * m_Samples[i].m_iPktsCount)); + + if (isFirstPeriod) + newRateBps = newRateBps * SAMPLE_DURATION_MS * NUM_PERIODS / max(1, startDelta); + + m_iRateBps = newRateBps; +} +} // namespace srt diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index 88479827d..ebc5e6b79 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -192,10 +192,87 @@ class CSndRateEstimator Sample m_Samples[NUM_PERIODS]; - time_point m_tsFirstSampleTime; //< Start time of the first sample. + time_point m_tsFirstSampleTime; //< Start time of the first sameple. int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; //< Rate in Bytes/sec. + int m_iRateBps; // Input Rate in Bytes/sec +}; + +class CMovingRateEstimator +{ + typedef sync::steady_clock::time_point time_point; + +public: + CMovingRateEstimator(); + + /// Add sample. + /// @param [in] pkts number of packets in the sample. + /// @param [in] bytes number of payload bytes in the sample. + void addSample(int pkts = 0, double bytes = 0); + + /// Clean the mobile measures table to reset average value. + void resetRate() { resetRate(0, NUM_PERIODS); }; + + /// Retrieve estimated bitrate in bytes per second with 16-byte packet header. + int getRate() const { return m_iRateBps; } + +private: + // We would like responsiveness (accuracy) of rate estimation higher than 100 ms + // (ideally around 50 ms) for network adaptive algorithms. + static const int NUM_PERIODS = 100; // To get 1s of values + static const int SAMPLE_DURATION_MS = 10; // 10 ms + time_point m_tsFirstSampleTime; //< Start time of the first sample. + time_point m_tsLastSlotTimestamp; // Used to compute the delta between 2 calls + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; //< Rate in Bytes/sec. + + struct Sample + { + int m_iPktsCount; // number of payload packets + int m_iBytesCount; // number of payload bytes + + void reset() + { + m_iPktsCount = 0; + m_iBytesCount = 0; + } + + Sample() + : m_iPktsCount(0) + , m_iBytesCount(0) + { + } + + Sample(int iPkts, int iBytes) + : m_iPktsCount(iPkts) + , m_iBytesCount(iBytes) + { + } + + Sample operator+(const Sample& other) + { + return Sample(m_iPktsCount + other.m_iPktsCount, m_iBytesCount + other.m_iBytesCount); + } + + Sample& operator+=(const Sample& other) + { + *this = *this + other; + return *this; + } + + bool empty() const { return m_iPktsCount == 0; } + }; + + srt::FixedArray m_Samples; // Table of stored data + + /// This method will compute the average value based on all table's measures and the period + /// (NUM_PERIODS*SAMPLE_DURATION_MS) + void computeAverageValue(); + + /// Reset a part of the stored measures + /// @param from The beginning where the reset have to be applied + /// @param to The last data that have to be reset + void resetRate(int from, int to); }; } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index de7723612..d335a955d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7665,10 +7665,15 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); + + // Average values management + // We are updating rate with 0 Byte 0 packet to ensure an up to date compute in case we are not sending packet for a while. + m_stats.sndr.updateRate(0, 0); + m_stats.rcvr.updateRate(0, 0); + perf->mbpsSendRate = Bps2Mbps(m_stats.sndr.getAverageValue()); + perf->mbpsRecvRate = Bps2Mbps(m_stats.rcvr.getAverageValue()); + // 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 = m_iCongestionWindow; @@ -9836,6 +9841,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime m_stats.sndr.sent.count(payload); if (new_packet_packed) m_stats.sndr.sentUnique.count(payload); + m_stats.sndr.updateRate(1, payload); leaveCS(m_StatsLock); const duration sendint = m_tdSendInterval; @@ -10520,6 +10526,7 @@ int srt::CUDT::processData(CUnit* in_unit) enterCS(m_StatsLock); m_stats.rcvr.recvd.count(pktsz); + m_stats.rcvr.updateRate(1, pktsz); leaveCS(m_StatsLock); loss_seqs_t filter_loss_seqs; diff --git a/srtcore/stats.h b/srtcore/stats.h index 947489eb1..4b67378fd 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -13,6 +13,7 @@ #include "platform_sys.h" #include "packet.h" +#include "buffer_tools.h" namespace srt { @@ -144,6 +145,8 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. + CMovingRateEstimator mavgRateEstimator; // The average Mbps over last second + void reset() { sent.reset(); @@ -167,6 +170,12 @@ struct Sender recvdNak.resetTrace(); sentFilterExtra.resetTrace(); } + + void updateRate(int pkts, double bytes) { mavgRateEstimator.addSample(pkts, bytes); } + + void resetRate() { mavgRateEstimator.resetRate(); } + + int getAverageValue() { return mavgRateEstimator.getRate(); } }; /// Receiver-side statistics. @@ -187,6 +196,8 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. + CMovingRateEstimator mavgRateEstimator; // The average Mbps over last second + void reset() { recvd.reset(); @@ -218,6 +229,12 @@ struct Receiver sentAck.resetTrace(); sentNak.resetTrace(); } + + void updateRate(int pkts, double bytes) { mavgRateEstimator.addSample(pkts, bytes); } + + void resetRate() { mavgRateEstimator.resetRate(); } + + int getAverageValue() { return mavgRateEstimator.getRate(); } }; } // namespace stats From 178a82b550d8642a77a31f8b9903eff67a433d48 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:44:51 +0100 Subject: [PATCH 154/174] [core] Fixed final HS dispatch to already accepted socket with the lack of listener (#1690) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core] Fixed a bug that responded a repeated conclusion HS with rejection * Fixed proper crafting of the repeated handshake response * Added crafting KMX response to call handshake * Fixed HS ext for HSv5. Blocked testing stuff * Fixed: rewritten negotiated values into the handshake and properly sent * Fixed: do not try accepted socket if wrong address * Changed blocked test code to conditionally-enabled --------- Co-authored-by: Mikołaj Małecki --- srtcore/core.cpp | 42 ++++++++++++++++++++++----- srtcore/core.h | 2 ++ srtcore/queue.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++ srtcore/queue.h | 10 +++++++ 4 files changed, 121 insertions(+), 7 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d335a955d..b3083ca8a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -50,6 +50,9 @@ modified by Haivision Systems Inc. *****************************************************************************/ +// Set this to 5 to fake "lost" handshake packets 5 times in a row +#define SRT_ENABLE_FAKE_LOSS_HS 0 + #include "platform_sys.h" // Linux specific @@ -4802,6 +4805,16 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. + // This part fictionally "loses" incoming conclusion HS given number of times. +#if SRT_ENABLE_FAKE_LOSS_HS > 0 + static int fail_count = SRT_ENABLE_FAKE_LOSS_HS; + if (--fail_count) + { + LOGC(cnlog.Note, log << "postConnect: FAKE LOSS HS conclusion message"); + return CONN_CONTINUE; + } +#endif + // This procedure isn't being executed in rendezvous because // in rendezvous it's completed before calling this function. if (!rendezvous) @@ -6005,6 +6018,21 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // Save the handshake in m_ConnRes in case when needs repeating. m_ConnRes = w_hs; + // NOTE: UNBLOCK THIS instruction in order to cause the final + // handshake to be missed and cause the problem solved in PR #417. + // When missed this message, the caller should not accept packets + // coming as connected, but continue repeated handshake until finally + // received the listener's handshake. + //return; + + if (!createSendHSResponse(kmdata, kmdatasize, hspkt.udpDestAddr(), (w_hs))) + { + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } +} + +bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW +{ // Send the response to the peer, see listen() for more discussions // about this. // TODO: Here create CONCLUSION RESPONSE with: @@ -6019,16 +6047,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, - log << CONID() - << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + log << CONID() << "createSendHSResponse: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { - LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); - throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + LOGC(cnlog.Error, log << CONID() << "createSendHSResponse: error creating handshake response"); + return false; } // We can safely assign it here stating that this has passed the cookie test. - m_SourceAddr = hspkt.udpDestAddr(); + m_SourceAddr = hsaddr; #if ENABLE_HEAVY_LOGGING { @@ -6037,8 +6064,8 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& CHandShake debughs; debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, - log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << rsppkt.id() + log << CONID() << "createSendHSResponse: sending HS from agent @" + << debughs.m_iID << " to peer @" << m_PeerID // <-- will be used by addressAndSend << "HS:" << debughs.show() << " sourceIP=" << m_SourceAddr.str()); } @@ -6050,6 +6077,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // coming as connected, but continue repeated handshake until finally // received the listener's handshake. addressAndSend((rsppkt)); + return true; } bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::string& w_why) diff --git a/srtcore/core.h b/srtcore/core.h index 8276ca452..e8d35b06b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -307,6 +307,7 @@ class CUDT } SRTSOCKET socketID() const { return m_SocketID; } + SRTSOCKET peerID() const { return m_PeerID; } static CUDT* getUDTHandle(SRTSOCKET u); static std::vector existingSockets(); @@ -598,6 +599,7 @@ class CUDT /// @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); + bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW; /// Write back to the hs structure the data after they have been /// negotiated by acceptAndRespond. diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index d02f42646..edf7a12bf 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -757,16 +757,27 @@ srt::CUDT* srt::CHash::lookup(int32_t id) return NULL; } +srt::CUDT* srt::CHash::lookupPeer(int32_t peerid) +{ + // Decode back the socket ID if it has that peer + int32_t id = map_get(m_RevPeerMap, peerid, -1); + if (id == -1) + return NULL; // no such peer id + return lookup(id); +} + void srt::CHash::insert(int32_t id, CUDT* u) { CBucket* b = m_pBucket[id % m_iHashSize]; CBucket* n = new CBucket; n->m_iID = id; + n->m_iPeerID = u->peerID(); n->m_pUDT = u; n->m_pNext = b; m_pBucket[id % m_iHashSize] = n; + m_RevPeerMap[u->peerID()] = id; } void srt::CHash::remove(int32_t id) @@ -783,6 +794,7 @@ void srt::CHash::remove(int32_t id) else p->m_pNext = b->m_pNext; + m_RevPeerMap.erase(b->m_iPeerID); delete b; return; @@ -1457,11 +1469,73 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } + else + { + if (worker_TryAcceptedSocket(unit, addr)) + { + HLOGC(cnlog.Debug, log << "connection request to an accepted socket succeeded"); + return CONN_CONTINUE; + } + else + { + HLOGC(cnlog.Debug, log << "connection request to an accepted socket failed. Will retry RDV or store"); + } + } // If there's no listener waiting for the packet, just store it into the queue. return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. } +bool srt::CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) +{ + // We are working with a possibly HS packet... check that. + CPacket& pkt = unit->m_Packet; + + if (pkt.getLength() < CHandShake::m_iContentSize || !pkt.isControl(UMSG_HANDSHAKE)) + return false; + + CHandShake hs; + if (0 != hs.load_from(pkt.data(), pkt.size())) + return false; + + if (hs.m_iReqType != URQ_CONCLUSION) + return false; + + if (hs.m_iVersion >= CUDT::HS_VERSION_SRT1) + hs.m_extension = true; + + // Ok, at last we have a peer ID info + int32_t peerid = hs.m_iID; + + // Now search for a socket that has this peer ID + CUDT* u = m_pHash->lookupPeer(peerid); + if (!u) + return false; // no socket has that peer in this multiplexer + + HLOGC(cnlog.Debug, log << "FOUND accepted socket @" << u->m_SocketID << " that is a peer for -@" + << peerid << " - DISPATCHING to it to resend HS response"); + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + if (u->craftKmResponse((kmdata), (kmdatasize)) != CONN_ACCEPT) + { + HLOGC(cnlog.Debug, log << "craftKmResponse: failed"); + return false; + } + + // addr should be unnecessary because the accepted socket should have + // it in its data. However, if it happened that this is a different + // address, do not send (could be some kind of attack). + if (addr != u->m_PeerAddr) + { + HLOGC(cnlog.Debug, log << "worker_TryAcceptedSocket: accepted socket has a different address: " + << u->m_PeerAddr.str() << " than the incoming HS request: " << addr.str() << " - POSSIBLE ATTACK"); + return false; + } + + return u->createSendHSResponse(kmdata, kmdatasize, pkt.udpDestAddr(), (hs)); +} + srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); diff --git a/srtcore/queue.h b/srtcore/queue.h index 5bb229cd5..166fe43a3 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -281,6 +281,12 @@ class CHash CUDT* lookup(int32_t id); + /// Look for a UDT instance from the hash table by source ID + /// @param [in] peerid socket ID of the peer reported as source ID + /// @return Pointer to a UDT instance where m_PeerID == peerid, or NULL if not found + + CUDT* lookupPeer(int32_t peerid); + /// Insert an entry to the hash table. /// @param [in] id socket ID /// @param [in] u pointer to the UDT instance @@ -296,6 +302,7 @@ class CHash struct CBucket { int32_t m_iID; // Socket ID + int32_t m_iPeerID; // Peer ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -303,6 +310,8 @@ class CHash int m_iHashSize; // size of hash table + std::map m_RevPeerMap; + private: CHash(const CHash&); CHash& operator=(const CHash&); @@ -521,6 +530,7 @@ class CRcvQueue 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); + bool worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr); private: CUnitQueue* m_pUnitQueue; // The received packet queue From 3d4b117935a151a7a2ceec704906476d35834854 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:46:34 +0100 Subject: [PATCH 155/174] [BUG] Fixed desynch between deleted socket and unsubscribing from epoll (misleading log flood) (#3090) Co-authored-by: Mikolaj Malecki --- srtcore/api.cpp | 18 +++++++++++++++++- srtcore/core.cpp | 37 +------------------------------------ srtcore/epoll.cpp | 27 +++++++++++++++++++++++++++ srtcore/epoll.h | 2 ++ test/test_bonding.cpp | 22 ++++++++++++++++++---- 5 files changed, 65 insertions(+), 41 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 910ff6082..a7f362bb8 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -872,6 +872,9 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, ns->removeFromGroup(true); } #endif + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(id, ns->core().m_sPollID); + m_Sockets.erase(id); m_ClosedSockets[id] = ns; } @@ -1735,6 +1738,9 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i targets[tii].errorcode = e.getErrorCode(); targets[tii].id = CUDT::INVALID_SOCK; + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(ns->m_SocketID, ns->core().m_sPollID); + ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); m_Sockets.erase(ns->m_SocketID); @@ -1749,6 +1755,8 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i targets[tii].id = CUDT::INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(ns->m_SocketID, ns->core().m_sPollID); m_Sockets.erase(ns->m_SocketID); // Intercept to delete the socket on failure. delete ns; @@ -2278,6 +2286,9 @@ int srt::CUDTUnited::close(CUDTSocket* s) } #endif + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(s->m_SocketID, s->core().m_sPollID); + m_Sockets.erase(s->m_SocketID); m_ClosedSockets[s->m_SocketID] = s; HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); @@ -3104,6 +3115,10 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; as->breakSocket_LOCKED(); + + // You won't be updating any EIDs anymore. + m_EPoll.wipe_usock(as->m_SocketID, as->core().m_sPollID); + m_ClosedSockets[q->first] = as; m_Sockets.erase(q->first); } @@ -3123,7 +3138,8 @@ 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->core().m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, false); + // (just in case, this should be wiped out already) + m_EPoll.wipe_usock(u, s->core().m_sPollID); // delete this one m_ClosedSockets.erase(i); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b3083ca8a..3902577c6 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6402,42 +6402,7 @@ bool srt::CUDT::closeInternal() ATR_NOEXCEPT * it would remove the socket from the EPoll after close. */ - // Make a copy under a lock because other thread might access it - // at the same time. - enterCS(uglobal().m_EPoll.m_EPollLock); - set epollid = m_sPollID; - leaveCS(uglobal().m_EPoll.m_EPollLock); - - // trigger any pending IO events. - HLOGC(smlog.Debug, log << CONID() << "close: SETTING ERR readiness on E" << Printable(epollid)); - 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) - { - HLOGC(smlog.Debug, log << CONID() << "close: CLEARING subscription on E" << (*i)); - try - { - uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events); - } - catch (...) - { - // The goal of this loop is to remove all subscriptions in - // the epoll system to this socket. If it's unsubscribed already, - // that's even better. - } - HLOGC(smlog.Debug, log << CONID() << "close: removing E" << (*i) << " from back-subscribers"); - } - - // Not deleting elements from m_sPollID inside the loop because it invalidates - // the control iterator of the loop. Instead, all will be removed at once. - - // 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(uglobal().m_EPoll.m_EPollLock); - m_sPollID.clear(); - leaveCS(uglobal().m_EPoll.m_EPollLock); + uglobal().m_EPoll.wipe_usock(m_SocketID, m_sPollID); // XXX What's this, could any of the above actions make it !m_bOpened? if (!m_bOpened) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 2c891f96b..3a607c3a1 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -966,6 +966,33 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return nupdated; } +/// This is a simple function which removes the socket from epoll system. +/// The subscription list should be provided in the @a eids container and +/// the socket is removed from each of them, then this is cleared. This +/// should be the socket's private EID container that keeps EIDs that it +/// should update when an appropriate event comes. +/// +/// @param uid Socket ID that has to be removed from the epoll system +/// @param eids EIDs that the given socket believes being subscribed in +void srt::CEPoll::wipe_usock(const SRTSOCKET uid, std::set& eids) +{ + ScopedLock pg (m_EPollLock); + for (set::iterator i = eids.begin(); i != eids.end(); ++ i) + { + map::iterator p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + HLOGC(eilog.Note, log << "epoll/wipe: E" << *i << " was deleted in the meantime"); + continue; + } + + CEPollDesc& ed = p->second; + ed.removeSubscription(uid); + } + + eids.clear(); +} + // Debug use only. #if ENABLE_HEAVY_LOGGING namespace srt diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 00d46ceb4..3ac190186 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -489,6 +489,8 @@ friend class srt::CRendezvousQueue; int update_events(const SRTSOCKET& uid, std::set& eids, int events, bool enable); + void wipe_usock(const SRTSOCKET uid, std::set& eids); + int setflags(const int eid, int32_t flags); private: diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 55ab26270..b7e1869ba 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "gtest/gtest.h" @@ -63,6 +64,8 @@ TEST(Bonding, SRTConnectGroup) #define EXPECT_SRT_SUCCESS(callform) EXPECT_NE(callform, -1) << "SRT ERROR: " << srt_getlasterror_str() +static std::mutex g_listening_stopped; + void listening_thread(bool should_read) { const SRTSOCKET server_sock = srt_create_socket(); @@ -119,6 +122,9 @@ void listening_thread(bool should_read) } } + std::cout << "Listen: wait for green light from the caller...\n"; + std::unique_lock listen_lock (g_listening_stopped); + srt_close(acp); srt_close(server_sock); @@ -136,8 +142,8 @@ int g_nfailed = 0; void ConnectCallback(void* , SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token) { std::cout << "Connect callback. Socket: " << sock - << ", error: " << error - << ", token: " << token << '\n'; + << ", error: " << error << " (" << srt_strerror(error, 0) + << "), token: " << token << '\n'; if (error == SRT_SUCCESS) ++g_nconnected; @@ -172,6 +178,10 @@ TEST(Bonding, NonBlockingGroupConnect) sockaddr_in safail = sa; safail.sin_port = htons(4201); // port where we have no listener + // We need to keep the listener with the socket without closing it + // until we are done. + std::unique_lock listen_lock (g_listening_stopped); + std::future listen_promise = std::async(std::launch::async, std::bind(&listening_thread, false)); std::cout << "Connecting two sockets " << std::endl; @@ -204,7 +214,7 @@ TEST(Bonding, NonBlockingGroupConnect) write, &wlen, 5000, /* timeout */ 0, 0, 0, 0); - + std::cout << "Epoll result: " << epoll_res << '\n'; std::cout << "Epoll rlen: " << rlen << ", wlen: " << wlen << '\n'; for (int i = 0; i < rlen; ++i) @@ -213,10 +223,14 @@ TEST(Bonding, NonBlockingGroupConnect) } for (int i = 0; i < wlen; ++i) { - std::cout << "Epoll write[" << i << "]: " << write[i] << " (removed from epoll)\n"; + SRT_SOCKSTATUS st = srt_getsockstate(write[i]); + std::cout << "Epoll write[" << i << "]: " << write[i] + << " ST:" << srt_logging::SockStatusStr(st) + << " (removing from epoll)\n"; EXPECT_EQ(srt_epoll_remove_usock(poll_id, write[i]), 0); } } + listen_lock.unlock(); // give green light to the listener so that it closes sockets. listen_promise.wait(); From b890a1d4c77e5385b15b20d30c973833192dae8d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:47:17 +0100 Subject: [PATCH 156/174] [API] Set up length limits for STREAMID and PACKETFILTER in the API (#3103) Co-authored-by: Mikolaj Malecki --- srtcore/socketconfig.h | 5 +++-- srtcore/srt.h | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index a07c5028b..1d4666489 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -56,6 +56,7 @@ written by #include #endif #include +#include "srt.h" #include "haicrypt.h" #include "congctl.h" #include "packet.h" @@ -214,8 +215,8 @@ struct CSrtConfig: CSrtMuxerConfig // Mimimum recv flight flag size is 32 packets 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_SID_LENGTH = SRT_STREAMID_MAX; + static const size_t MAX_PFILTER_LENGTH = SRT_PACKETFILTER_MAX; static const size_t MAX_CONG_LENGTH = 16; int iMSS; // Maximum Segment Size, in bytes diff --git a/srtcore/srt.h b/srtcore/srt.h index 9efac1704..bf89fb257 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -303,6 +303,12 @@ static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - U // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; +// Maximum number of characters for SRTO_STREAMID +static const size_t SRT_STREAMID_MAX = 512; + +// Maximum number of characters for packet filter configuration, SRTO_PACKETFILTER +static const size_t SRT_PACKETFILTER_MAX = 64; + // Importrant note: please add new fields to this structure to the end and don't remove any existing fields struct CBytePerfMon { From 4ce1757260c11b0449528945dc1de4633d39f9b4 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 11:50:56 +0100 Subject: [PATCH 157/174] [MAINT] Added optional test discovery. Added improvements for attributes (#3132) Co-authored-by: Mikolaj Malecki --- CMakeLists.txt | 5 +++- srtcore/srt.h | 4 +++ srtcore/srt_attr_defs.h | 61 +++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11f112540..1c679c130 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) option(ENABLE_UNITTESTS "Enable unit tests" OFF) +option(ENABLE_UNITTESTS_DISCOVERY "Do not discover unit tests when enabled" ON) option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off) option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off) @@ -1583,7 +1584,9 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - gtest_discover_tests(test-srt) + if (ENABLE_UNITTESTS_DISCOVERY) + gtest_discover_tests(test-srt) + endif() endif() enable_testing() diff --git a/srtcore/srt.h b/srtcore/srt.h index bf89fb257..38dafe605 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -109,17 +109,21 @@ written by #define SRT_ATR_DEPRECATED #define SRT_ATR_DEPRECATED_PX [[deprecated]] +#define SRT_ATR_NODISCARD [[nodiscard]] // GNUG is GNU C/C++; this syntax is also supported by Clang #elif defined(__GNUC__) #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED __attribute__((deprecated)) +#define SRT_ATR_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) #define SRT_ATR_DEPRECATED_PX __declspec(deprecated) #define SRT_ATR_DEPRECATED // no postfix-type modifier +#define SRT_ATR_NODISCARD _Check_return_ #else #define SRT_ATR_DEPRECATED_PX #define SRT_ATR_DEPRECATED +#define SRT_ATR_NODISCARD #endif #ifdef __cplusplus diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 726c4a03b..2c71d80d5 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -17,26 +17,17 @@ used by SRT library internally. // 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 HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#ifdef __GNUG__ +#define HAVE_GCC 1 #else -#define SRT_ATR_ALIGNAS(n) +#define HAVE_GCC 0 #endif #if defined(__cplusplus) && __cplusplus > 199711L @@ -44,18 +35,15 @@ used by SRT library internally. // 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 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + #endif #elif defined(_MSC_VER) && _MSC_VER >= 1800 // Microsoft Visual Studio supports C++11, but not fully, @@ -65,26 +53,41 @@ used by SRT library internally. #define HAVE_CXX11 1 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 #define HAVE_FULL_CXX11 1 + +#if __cplusplus > 201406 +#define HAVE_CXX17 1 +#else +#define HAVE_CXX17 0 +#endif + +#endif +#else +#define HAVE_CXX11 0 +#define HAVE_CXX17 0 +#endif // __cplusplus + +#if HAVE_FULL_CXX11 #define ATR_NOEXCEPT noexcept #define ATR_NOTHROW noexcept #define ATR_CONSTEXPR constexpr #define ATR_OVERRIDE override #define ATR_FINAL final #else +// These are both for HAVE_CXX11 == 1 and 0. #define ATR_NOEXCEPT #define ATR_NOTHROW throw() #define ATR_CONSTEXPR #define ATR_OVERRIDE #define ATR_FINAL #endif + +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) #else -#define HAVE_CXX11 0 -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#endif // __cplusplus +#define SRT_ATR_ALIGNAS(n) +#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." From 8330296bbd470999b8ff0eadfdb2981a04672718 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 19 Mar 2025 12:15:28 +0100 Subject: [PATCH 158/174] Post-fix for invalidly resolved merge conflict --- srtcore/core.cpp | 2 +- srtcore/core.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 3902577c6..2747ad688 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6031,7 +6031,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } } -bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW +bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const CNetworkInterface& hsaddr, CHandShake& w_hs) ATR_NOTHROW { // Send the response to the peer, see listen() for more discussions // about this. diff --git a/srtcore/core.h b/srtcore/core.h index e8d35b06b..501f6f352 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -599,7 +599,7 @@ class CUDT /// @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); - bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& hsaddr, CHandShake& w_hs) ATR_NOTHROW; + bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const CNetworkInterface& hsaddr, CHandShake& w_hs) ATR_NOTHROW; /// Write back to the hs structure the data after they have been /// negotiated by acceptAndRespond. From 314b6d225ede27ab7236c4f4de392b03e7f43c0e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 Mar 2025 12:31:56 +0100 Subject: [PATCH 159/174] [API] Removed setting +W for a listener socket after accept ready (#2732). (#3069) Co-authored-by: Sektor van Skijlen --- srtcore/core.cpp | 21 ++++++--------------- testing/testmedia.cpp | 4 ++-- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2747ad688..7bede5a4a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11150,6 +11150,11 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); + // NOTE (IMPORTANT!!!) + // + // The current CUDT object represents a LISTENER SOCKET to which + // the request was redirected from the receiver queue. + if (m_bClosing) { m_RejectReason = SRT_REJ_CLOSE; @@ -11434,21 +11439,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) } } - if (result == 1) - { - // BUG! There is no need to update write-readiness on the listener socket once new connection is accepted. - // Only read-readiness has to be updated, but it is done so in the newConnection(..) function. - // See PR #1831 and issue #1667. - HLOGC(cnlog.Debug, - log << CONID() << "processConnectRequest: accepted connection, updating epoll to write-ready"); - - // New connection has been accepted or an existing one has been found. Update epoll write-readiness. - // a new connection has been created, enable epoll for write - // Note: not using SRT_EPOLL_CONNECT symbol because this is a procedure - // executed for the accepted socket. - uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); - } - else if (result == -1) + if (result == -1) { // The new connection failed // or the connection already existed, but manually sending the HS response above has failed. diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 9aeeadc20..097bcb150 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -535,7 +535,7 @@ void SrtCommon::PrepareListener(string host, int port, int backlog) if (!m_blocking_mode) { - srt_conn_epoll = AddPoller(m_bindsock, SRT_EPOLL_OUT); + srt_conn_epoll = AddPoller(m_bindsock, SRT_EPOLL_IN); } auto sa = CreateAddr(host, port); @@ -584,7 +584,7 @@ void SrtCommon::AcceptNewClient() int len = 2; SRTSOCKET ready[2]; - while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == -1) + while (srt_epoll_wait(srt_conn_epoll, ready, &len, 0, 0, 1000, 0, 0, 0, 0) == -1) { if (::transmit_int_state) Error("srt_epoll_wait for srt_accept: interrupt"); From 20cef08f93d2096c13e794f6802431c9cd4afaef Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 14:32:04 +0100 Subject: [PATCH 160/174] Added messages for access control rejection messages (#2606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BUG] Fixed default reject reason for a listener callback * Added extended reject reason string API * Fixed testing app to set unauthorized rejection code for a better example * Removed deprecated remapping array --------- Co-authored-by: Mikołaj Małecki --- srtcore/access_control.h | 2 +- srtcore/srt.h | 4 +- srtcore/srt_c_api.cpp | 85 ++++++++++++++++++++++++++++----------- testing/srt-test-live.cpp | 5 ++- testing/testmedia.cpp | 21 ++++++++-- 5 files changed, 84 insertions(+), 33 deletions(-) diff --git a/srtcore/access_control.h b/srtcore/access_control.h index 611e1dad8..18357aeee 100644 --- a/srtcore/access_control.h +++ b/srtcore/access_control.h @@ -66,7 +66,7 @@ written by #define SRT_REJX_GW 1502 // The server acts as a gateway and the target endpoint rejected the connection. #define SRT_REJX_DOWN 1503 // The service has been temporarily taken over by a stub reporting this error. The real service can be down for maintenance or crashed. // CODE NOT IN USE 504: unused: timeout not supported -#define SRT_REJX_VERSION 1505 // SRT version not supported. This might be either unsupported backward compatibility, or an upper value of a version. +#define SRT_REJX_VERSION 1505 // Version not supported - either SRT or the application itself. This might be either unsupported backward compatibility, or an upper value of a version. // CODE NOT IN USE 506: unused: negotiation and references not supported #define SRT_REJX_NOROOM 1507 // The data stream cannot be archived due to lacking storage space. This is in case when the request type was to send a file or the live stream to be archived. // CODE NOT IN USE 508: unused: no redirection supported diff --git a/srtcore/srt.h b/srtcore/srt.h index 38dafe605..fa77054f8 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -932,10 +932,8 @@ 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); -// The srt_rejectreason_msg[] array is deprecated (as unsafe). -// Planned removal: v1.6.0. -SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg []; SRT_API const char* srt_rejectreason_str(int id); +SRT_API const char* srt_rejectreasonx_str(int id); SRT_API uint32_t srt_getversion(void); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 3c002d4a1..8a3e81323 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -17,7 +17,10 @@ written by #include #include +#include +#include #include "srt.h" +#include "access_control.h" #include "common.h" #include "packet.h" #include "core.h" @@ -446,32 +449,14 @@ const char* const srt_rejection_reason_msg [] = { "Invalid configuration" }; -// Deprecated, available in SRT API. -extern const char* const srt_rejectreason_msg[] = { - srt_rejection_reason_msg[0], - srt_rejection_reason_msg[1], - srt_rejection_reason_msg[2], - srt_rejection_reason_msg[3], - srt_rejection_reason_msg[4], - srt_rejection_reason_msg[5], - srt_rejection_reason_msg[6], - srt_rejection_reason_msg[7], - srt_rejection_reason_msg[8], - srt_rejection_reason_msg[9], - srt_rejection_reason_msg[10], - srt_rejection_reason_msg[11], - srt_rejection_reason_msg[12], - srt_rejection_reason_msg[13], - srt_rejection_reason_msg[14], - srt_rejection_reason_msg[15], - srt_rejection_reason_msg[16], - srt_rejection_reason_msg[17], - srt_rejection_reason_msg[18] -}; - const char* srt_rejectreason_str(int id) { using namespace srt_logging; + if (id == SRT_REJX_FALLBACK) + { + return "Application fallback (default) rejection reason"; + } + if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -486,4 +471,58 @@ const char* srt_rejectreason_str(int id) return srt_rejection_reason_msg[id]; } +// NOTE: values in the first field must be sorted by numbers. +static pair srt_rejectionx_reason_msg [] = { + + // Internal + make_pair(SRT_REJX_FALLBACK, "Default fallback reason"), + make_pair(SRT_REJX_KEY_NOTSUP, "Unsupported streamid key"), + make_pair(SRT_REJX_FILEPATH, "Incorrect resource path"), + make_pair(SRT_REJX_HOSTNOTFOUND, "Unrecognized host under h key"), + + // HTTP adopted codes + make_pair(SRT_REJX_BAD_REQUEST, "Bad request"), + make_pair(SRT_REJX_UNAUTHORIZED, "Unauthorized"), + make_pair(SRT_REJX_OVERLOAD, "Server overloaded or underpaid"), + make_pair(SRT_REJX_FORBIDDEN, "Resource access forbidden"), + make_pair(SRT_REJX_BAD_MODE, "Bad mode specified with m key"), + make_pair(SRT_REJX_UNACCEPTABLE, "Unacceptable parameters for specified resource"), + make_pair(SRT_REJX_CONFLICT, "Access conflict for a locked resource"), + make_pair(SRT_REJX_NOTSUP_MEDIA, "Unsupported media type specified with t key"), + make_pair(SRT_REJX_LOCKED, "Resource locked for any access"), + make_pair(SRT_REJX_FAILED_DEPEND, "Dependent session id expired"), + make_pair(SRT_REJX_ISE, "Internal server error"), + make_pair(SRT_REJX_GW, "Gateway target rejected connection"), + make_pair(SRT_REJX_DOWN, "Service is down for maintenance"), + make_pair(SRT_REJX_VERSION, "Unsupported version for the request"), + make_pair(SRT_REJX_NOROOM, "Storage capacity exceeded"), +}; + +struct FCompareItems +{ + bool operator()(const pair& a, int b) + { + return a.first < b; + } +}; + +const char* srt_rejectreasonx_str(int id) +{ + if (id < SRT_REJX_FALLBACK) + { + return "System-defined rejection reason (not extended)"; + } + + pair* begin = srt_rejectionx_reason_msg; + pair* end = begin + Size(srt_rejectionx_reason_msg); + pair* found = lower_bound(begin, end, id, FCompareItems()); + + if (found == end || found->first != id) + { + return "Undefined extended rejection code"; + } + + return found->second; +} + } diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 12478e461..25401f0ff 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -72,11 +72,11 @@ #include "testmedia.hpp" // requires access to SRT-dependent globals #include "verbose.hpp" -// NOTE: This is without "haisrt/" because it uses an internal path +// NOTE: This is without "srt/" because it uses an internal path // to the library. Application using the "installed" library should // use #include -#include // This TEMPORARILY contains extra C++-only SRT API. +#include #include using namespace std; @@ -355,6 +355,7 @@ extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const soc // This hook sets the password to the just accepted socket // depending on the user + srt_setrejectreason(acpsock, SRT_REJX_UNAUTHORIZED); string exp_pw = passwd.at(username); srt_setsockflag(acpsock, SRTO_PASSPHRASE, exp_pw.c_str(), int(exp_pw.size())); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 097bcb150..679756e1d 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -98,6 +98,19 @@ string DirectionName(SRT_EPOLL_T direction) return dir_name; } +static string RejectReasonStr(int id) +{ + if (id < SRT_REJC_PREDEFINED) + return srt_rejectreason_str(id); + + if (id < SRT_REJC_USERDEFINED) + return srt_rejectreasonx_str(id); + + ostringstream sout; + sout << "User-defined reason code " << id; + return sout.str(); +} + template inline bytevector FileRead(FileBase& ifile, size_t chunk, const string& filename) { @@ -1149,7 +1162,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1273,7 +1286,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1520,11 +1533,11 @@ void SrtCommon::Error(string src, int reason, int force_result) if ( Verbose::on ) Verb() << "FAILURE\n" << src << ": [" << result << "] " << "Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); else cerr << "\nERROR #" << result << ": Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); } else { From 1b050ade0b343a529dfbd03b1eaf2c6de435f481 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 15:45:38 +0100 Subject: [PATCH 161/174] [BUG] Fixed the use of the right error constant (#2595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixing implicit integer and wrong use of SRT_ERROR and SRT_INVALID_SOCKET * Fixed wrong assertion for drop snd buf sequence check * [MAINT] Refax for CSndBuffer::readData to make the call form clearer * Introduced SRTRUNSTATUS to cover all cases of srt_startup(). Fixed documentation. * Removed srt::isgroup. Reused CUDT::isgroup where applicable. Added missing license heading. * Removed unreachable code --------- Co-authored-by: Mikołaj Małecki --- CMakeLists.txt | 2 +- apps/apputil.hpp | 4 +- apps/socketoptions.hpp | 14 +- apps/srt-file-transmit.cpp | 14 +- apps/srt-live-transmit.cpp | 14 +- apps/srt-tunnel.cpp | 22 +- apps/statswriter.cpp | 6 +- apps/statswriter.hpp | 2 +- apps/transmitmedia.cpp | 71 ++--- apps/transmitmedia.hpp | 6 +- common/devel_util.h | 118 +++++++ docs/API/API-functions.md | 419 +++++++++++------------- examples/sendmsg.cpp | 2 +- srtcore/api.cpp | 510 +++++++++++++++--------------- srtcore/api.h | 62 ++-- srtcore/buffer_rcv.cpp | 4 + srtcore/core.cpp | 76 +++-- srtcore/core.h | 75 +++-- srtcore/crypto.cpp | 2 +- srtcore/epoll.cpp | 30 +- srtcore/epoll.h | 23 +- srtcore/fec.cpp | 2 +- srtcore/group.cpp | 32 +- srtcore/group.h | 6 +- srtcore/handshake.cpp | 4 +- srtcore/handshake.h | 2 +- srtcore/queue.cpp | 68 ++-- srtcore/queue.h | 38 ++- srtcore/srt.h | 119 ++++--- srtcore/srt_c_api.cpp | 115 +++---- srtcore/tsbpd_time.cpp | 2 +- srtcore/udt.h | 40 +-- test/test_connection_timeout.cpp | 30 +- test/test_enforced_encryption.cpp | 192 +++++------ test/test_epoll.cpp | 184 +++++------ test/test_fec_rebuilding.cpp | 14 +- test/test_file_transmission.cpp | 9 +- test/test_ipv6.cpp | 4 +- test/test_main.cpp | 4 +- testing/srt-test-file.cpp | 6 +- testing/srt-test-live.cpp | 2 +- testing/testmedia.cpp | 134 ++++---- testing/testmedia.hpp | 10 +- 43 files changed, 1326 insertions(+), 1167 deletions(-) create mode 100644 common/devel_util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c679c130..38ed9e716 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -731,7 +731,7 @@ if (FORCE_CXX_STANDARD) endif() # add extra warning flags for gccish compilers -if (HAVE_COMPILER_GNU_COMPAT) +if ((HAVE_COMPILER_GNU_COMPAT) AND (NOT DEFINED SRT_GCC_WARN)) set (SRT_GCC_WARN "-Wall -Wextra") if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set (SRT_GCC_WARN "${SRT_GCC_WARN} -Wshadow=local") diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 707a816e8..9242f0c36 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -345,7 +345,7 @@ namespace srt struct OptionSetterProxy { SRTSOCKET s; - int result = -1; + SRTSTATUS result = SRT_ERROR; OptionSetterProxy(SRTSOCKET ss): s(ss) {} @@ -385,7 +385,7 @@ struct OptionSetterProxy return OptionProxy {*this, opt}; } - operator int() { return result; } + operator SRTSTATUS() { return result; } }; inline OptionSetterProxy setopt(SRTSOCKET socket) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index b8aa67b86..e6de4db17 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -58,23 +58,27 @@ struct SocketOption bool applyt(Object socket, std::string value) const; template - static int setso(Object socket, int protocol, int symbol, const void* data, size_t size); + static int setso(Object , int , int , const void* , size_t) + { + typename Object::wrong_version error; + return -1; + } template bool extract(std::string value, OptionValue& val) const; }; template<> -inline int SocketOption::setso(int socket, int /*ignored*/, int sym, const void* data, size_t size) +inline int SocketOption::setso(SRTSOCKET socket, int /*ignored*/, int sym, const void* data, size_t size) { - return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_setsockflag(socket, SRT_SOCKOPT(sym), data, (int) size); } #if ENABLE_BONDING template<> inline int SocketOption::setso(SRT_SOCKOPT_CONFIG* obj, int /*ignored*/, int sym, const void* data, size_t size) { - return srt_config_add(obj, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_config_add(obj, SRT_SOCKOPT(sym), data, (int) size); } #endif @@ -184,7 +188,7 @@ inline bool SocketOption::applyt(Object socket, std::string value) const int result = -1; if (extract(value, o)) result = setso(socket, protocol, symbol, o.value, o.size); - return result != -1; + return result != int(SRT_ERROR); } diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index e29ae3248..e5b76784c 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -309,7 +309,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -349,7 +349,7 @@ bool DoUpload(UriParser& ut, string path, string filename, s = tar->GetSRTSocket(); int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -391,7 +391,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int st = tar->Write(buf.data() + shift, n, 0, out_stats); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; @@ -429,7 +429,7 @@ bool DoUpload(UriParser& ut, string path, string filename, size_t bytes; size_t blocks; int st = srt_getsndbuffer(s, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; @@ -490,7 +490,7 @@ bool DoDownload(UriParser& us, string directory, string filename, int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -528,7 +528,7 @@ bool DoDownload(UriParser& us, string directory, string filename, s = src->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -593,7 +593,7 @@ bool DoDownload(UriParser& us, string directory, string filename, } int n = src->Read(cfg.chunk_size, packet, out_stats); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; goto exit; diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 932bcd142..3a12a1a71 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -519,7 +519,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -529,7 +529,7 @@ int main(int argc, char** argv) case UriParser::UDP: case UriParser::RTP: if (srt_epoll_add_ssock(pollid, - src->GetSysSocket(), &events)) + src->GetSysSocket(), &events) == SRT_ERROR) { cerr << "Failed to add " << src->uri.proto() << " source to poll, " << src->GetSysSocket() @@ -541,7 +541,7 @@ int main(int argc, char** argv) { const int con = src->GetSysSocket(); // try to make the standard input non blocking - if (srt_epoll_add_ssock(pollid, con, &events)) + if (srt_epoll_add_ssock(pollid, con, &events) == SRT_ERROR) { cerr << "Failed to add FILE source to poll, " << src->GetSysSocket() << endl; @@ -573,7 +573,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -647,7 +647,7 @@ int main(int argc, char** argv) SRTSOCKET ns = (issource) ? src->GetSRTSocket() : tar->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, ns, &events)) + if (srt_epoll_add_usock(pollid, ns, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll, " << ns << endl; @@ -745,7 +745,7 @@ int main(int argc, char** argv) const int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; // Disable OUT event polling when connected if (srt_epoll_update_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -811,7 +811,7 @@ int main(int argc, char** argv) std::shared_ptr pkt(new MediaPacket(transmit_chunk_size)); const int res = src->Read(transmit_chunk_size, *pkt, out_stats); - if (res == SRT_ERROR && src->uri.type() == UriParser::SRT) + if (res == int(SRT_ERROR) && src->uri.type() == UriParser::SRT) { if (srt_getlasterror(NULL) == SRT_EASYNCRCV) break; diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 90bd43eae..060256351 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -420,7 +420,7 @@ void Engine::Worker() class SrtMedium: public Medium { - SRTSOCKET m_socket = SRT_ERROR; + SRTSOCKET m_socket = SRT_INVALID_SOCK; friend class Medium; public: @@ -441,10 +441,10 @@ class SrtMedium: public Medium { Verb() << "Closing SRT socket for " << uri(); lock_guard lk(access); - if (m_socket == SRT_ERROR) + if (m_socket == SRT_INVALID_SOCK) return; srt_close(m_socket); - m_socket = SRT_ERROR; + m_socket = SRT_INVALID_SOCK; } // Forwarded in order to separate the implementation from @@ -625,16 +625,16 @@ void SrtMedium::CreateListener() sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int stat = srt_bind(m_socket, sa.get(), sizeof sa); + SRTSTATUS stat = srt_bind(m_socket, sa.get(), sizeof sa); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_bind"); } stat = srt_listen(m_socket, backlog); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_listen"); @@ -675,7 +675,7 @@ unique_ptr SrtMedium::Accept() { sockaddr_any sa; SRTSOCKET s = srt_accept(m_socket, (sa.get()), (&sa.len)); - if (s == SRT_ERROR) + if (s == SRT_INVALID_SOCK) { Error(UDT::getlasterror(), "srt_accept"); } @@ -735,8 +735,8 @@ void SrtMedium::Connect() { sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int st = srt_connect(m_socket, sa.get(), sizeof sa); - if (st == SRT_ERROR) + SRTSOCKET st = srt_connect(m_socket, sa.get(), sizeof sa); + if (st == SRT_INVALID_SOCK) Error(UDT::getlasterror(), "srt_connect"); ConfigurePost(m_socket); @@ -767,7 +767,7 @@ int SrtMedium::ReadInternal(char* w_buffer, int size) do { st = srt_recv(m_socket, (w_buffer), size); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { int syserr; if (srt_getlasterror(&syserr) == SRT_EASYNCRCV && !m_broken) @@ -886,7 +886,7 @@ Medium::ReadStatus Medium::Read(bytevector& w_output) void SrtMedium::Write(bytevector& w_buffer) { int st = srt_send(m_socket, w_buffer.data(), (int)w_buffer.size()); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { Error(UDT::getlasterror(), "srt_send"); } diff --git a/apps/statswriter.cpp b/apps/statswriter.cpp index f1aa343b5..7fdf54bcb 100644 --- a/apps/statswriter.cpp +++ b/apps/statswriter.cpp @@ -146,7 +146,7 @@ class SrtStatsJson : public SrtStatsWriter } public: - string WriteStats(int sid, const CBytePerfMon& mon) override + string WriteStats(SRTSOCKET sid, const CBytePerfMon& mon) override { std::ostringstream output; @@ -235,7 +235,7 @@ class SrtStatsCsv : public SrtStatsWriter public: SrtStatsCsv() : first_line_printed(false) {} - string WriteStats(int sid, const CBytePerfMon& mon) override + string WriteStats(SRTSOCKET sid, const CBytePerfMon& mon) override { std::ostringstream output; @@ -286,7 +286,7 @@ class SrtStatsCsv : public SrtStatsWriter class SrtStatsCols : public SrtStatsWriter { public: - string WriteStats(int sid, const CBytePerfMon& mon) override + string WriteStats(SRTSOCKET sid, const CBytePerfMon& mon) override { std::ostringstream output; output << "======= SRT STATS: sid=" << sid << endl; diff --git a/apps/statswriter.hpp b/apps/statswriter.hpp index 1b4fd4fbc..0babc7a00 100644 --- a/apps/statswriter.hpp +++ b/apps/statswriter.hpp @@ -71,7 +71,7 @@ struct SrtStatDataType: public SrtStatData class SrtStatsWriter { public: - virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; + virtual std::string WriteStats(SRTSOCKET sid, const CBytePerfMon& mon) = 0; virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; virtual ~SrtStatsWriter() {} diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 0a656493e..025df1229 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -248,11 +248,11 @@ void SrtCommon::InitParameters(string host, map par) void SrtCommon::PrepareListener(string host, int port, int backlog) { m_bindsock = srt_create_socket(); - if ( m_bindsock == SRT_ERROR ) + if (m_bindsock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_bindsock); - if ( stat == SRT_ERROR ) + SRTSTATUS stat = ConfigurePre(m_bindsock); + if (stat == SRT_ERROR) Error("ConfigurePre"); sockaddr_any sa = CreateAddr(host, port); @@ -260,7 +260,7 @@ void SrtCommon::PrepareListener(string host, int port, int backlog) Verb() << "Binding a server on " << host << ":" << port << " ..."; stat = srt_bind(m_bindsock, psa, sizeof sa); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_bindsock); Error("srt_bind"); @@ -269,7 +269,7 @@ void SrtCommon::PrepareListener(string host, int port, int backlog) Verb() << " listen..."; stat = srt_listen(m_bindsock, backlog); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_bindsock); Error("srt_listen"); @@ -304,7 +304,7 @@ bool SrtCommon::AcceptNewClient() } int maxsize = srt_getmaxpayloadsize(m_sock); - if (maxsize == SRT_ERROR) + if (maxsize == int(SRT_ERROR)) { srt_close(m_bindsock); srt_close(m_sock); @@ -327,8 +327,8 @@ bool SrtCommon::AcceptNewClient() // ConfigurePre is done on bindsock, so any possible Pre flags // are DERIVED by sock. ConfigurePost is done exclusively on sock. - int stat = ConfigurePost(m_sock); - if ( stat == SRT_ERROR ) + SRTSTATUS stat = ConfigurePost(m_sock); + if (stat == SRT_ERROR) Error("ConfigurePost"); return true; @@ -354,14 +354,14 @@ void SrtCommon::Init(string host, int port, map par, bool dir_out } } -int SrtCommon::ConfigurePost(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePost(SRTSOCKET sock) { bool no = false; - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; if ( m_output_direction ) { result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &no, sizeof no); - if ( result == -1 ) + if ( result == SRT_ERROR ) return result; if ( m_timeout ) @@ -370,7 +370,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) else { result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &no, sizeof no); - if ( result == -1 ) + if ( result == SRT_ERROR ) return result; if ( m_timeout ) @@ -394,23 +394,23 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) } } - return 0; + return SRT_STATUS_OK; } -int SrtCommon::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePre(SRTSOCKET sock) { - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; bool no = false; if ( !m_tsbpdmode ) { result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no); - if ( result == -1 ) + if ( result == SRT_ERROR ) return result; } result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &no, sizeof no); - if ( result == -1 ) + if ( result == SRT_ERROR ) return result; @@ -435,14 +435,14 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } void SrtCommon::SetupAdapter(const string& host, int port) { sockaddr_any localsa = CreateAddr(host, port); sockaddr* psa = localsa.get(); - int stat = srt_bind(m_sock, psa, sizeof localsa); + SRTSTATUS stat = srt_bind(m_sock, psa, sizeof localsa); if ( stat == SRT_ERROR ) Error("srt_bind"); } @@ -462,10 +462,10 @@ void SrtCommon::OpenClient(string host, int port) void SrtCommon::PrepareClient() { m_sock = srt_create_socket(); - if ( m_sock == SRT_ERROR ) + if ( m_sock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_sock); + SRTSTATUS stat = ConfigurePre(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePre"); } @@ -473,21 +473,20 @@ void SrtCommon::PrepareClient() void SrtCommon::ConnectClient(string host, int port) { - sockaddr_any sa = CreateAddr(host, port); sockaddr* psa = sa.get(); Verb() << "Connecting to " << host << ":" << port; - int stat = srt_connect(m_sock, psa, sizeof sa); - if ( stat == SRT_ERROR ) + SRTSOCKET cstat = srt_connect(m_sock, psa, sizeof sa); + if (cstat == SRT_INVALID_SOCK) { srt_close(m_sock); Error("srt_connect"); } int maxsize = srt_getmaxpayloadsize(m_sock); - if (maxsize == SRT_ERROR) + if (maxsize == int(SRT_ERROR)) { srt_close(m_sock); Error("srt_getmaxpayloadsize"); @@ -499,7 +498,7 @@ void SrtCommon::ConnectClient(string host, int port) Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); } - stat = ConfigurePost(m_sock); + SRTSTATUS stat = ConfigurePost(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePost"); } @@ -517,13 +516,13 @@ void SrtCommon::Error(string src) void SrtCommon::OpenRendezvous(string adapter, string host, int port) { m_sock = srt_create_socket(); - if ( m_sock == SRT_ERROR ) + if (m_sock == SRT_INVALID_SOCK) Error("srt_create_socket"); bool yes = true; srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); - int stat = ConfigurePre(m_sock); + SRTSTATUS stat = ConfigurePre(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePre"); @@ -548,8 +547,8 @@ void SrtCommon::OpenRendezvous(string adapter, string host, int port) Verb() << "Connecting to " << host << ":" << port; - stat = srt_connect(m_sock, sa.get(), sizeof sa); - if ( stat == SRT_ERROR ) + SRTSOCKET cstat = srt_connect(m_sock, sa.get(), sizeof sa); + if ( cstat == SRT_INVALID_SOCK) { srt_close(m_sock); Error("srt_connect"); @@ -633,10 +632,10 @@ int SrtSource::Read(size_t chunk, MediaPacket& pkt, ostream &out_stats) return stat; } -int SrtTarget::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtTarget::ConfigurePre(SRTSOCKET sock) { - int result = SrtCommon::ConfigurePre(sock); - if ( result == -1 ) + SRTSTATUS result = SrtCommon::ConfigurePre(sock); + if ( result == SRT_ERROR ) return result; int yes = 1; @@ -645,10 +644,10 @@ int SrtTarget::ConfigurePre(SRTSOCKET sock) // In HSv4 this setting is obligatory; otherwise the SRT handshake // extension will not be done at all. result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes); - if ( result == -1 ) + if ( result == SRT_ERROR ) return result; - return 0; + return SRT_STATUS_OK; } int SrtTarget::Write(const char* data, size_t size, int64_t src_time, ostream &out_stats) @@ -658,7 +657,7 @@ int SrtTarget::Write(const char* data, size_t size, int64_t src_time, ostream &o SRT_MSGCTRL ctrl = srt_msgctrl_default; ctrl.srctime = src_time; int stat = srt_sendmsg2(m_sock, data, (int) size, &ctrl); - if (stat == SRT_ERROR) + if (stat == int(SRT_ERROR)) { return stat; } diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index a4812d815..1da244340 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -64,8 +64,8 @@ class SrtCommon void Error(string src); void Init(string host, int port, map par, bool dir_output); - virtual int ConfigurePost(SRTSOCKET sock); - virtual int ConfigurePre(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePost(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePre(SRTSOCKET sock); void OpenClient(string host, int port); void PrepareClient(); @@ -133,7 +133,7 @@ class SrtTarget: public Target, public SrtCommon SrtTarget() {} - int ConfigurePre(SRTSOCKET sock) override; + SRTSTATUS ConfigurePre(SRTSOCKET sock) override; 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(); } diff --git a/common/devel_util.h b/common/devel_util.h new file mode 100644 index 000000000..4e31af07e --- /dev/null +++ b/common/devel_util.h @@ -0,0 +1,118 @@ +/* + * 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/. + * + */ + +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// IMPORTANT!!! +// +// This is normally not a part of SRT source files. This is a developer utility +// that allows developers to perform a compile test on a version instrumented +// with type checks. To do that, you can do one of two things: +// +// - configure the compiling process with extra -DSRT_TEST_FORCED_CONSTANT=1 flag +// - unblock the commented out #define in srt.h file for that constant +// +// Note that there's no use of such a compiled code. This is done only so that +// the compiler can detect any misuses of the SRT symbolic type names and +// constants. + + +#include + +template +concept Streamable = requires(OS& os, T value) { + { os << value }; +}; + +template +struct IntWrapper +{ + INT v; + + IntWrapper() {} + explicit IntWrapper(INT val): v(val) {} + + bool operator==(const IntWrapper& x) const + { + return v == x.v; + } + + bool operator!=(const IntWrapper& x) const + { + return !(*this == x); + } + + explicit operator INT() const + { + return v; + } + + bool operator<(const IntWrapper& w) const + { + return v < w.v; + } + + template + requires Streamable + friend Str& operator<<(Str& out, const IntWrapper& x) + { + out << x.v; + return out; + } + + friend std::ostream& operator<<(std::ostream& out, const IntWrapper& x) + { + out << x.v; + return out; + } +}; + +template +struct IntWrapperLoose: IntWrapper +{ + typedef IntWrapper base_t; + explicit IntWrapperLoose(INT val): base_t(val) {} + + bool operator==(const IntWrapper& x) const + { + return this->v == x.v; + } + + friend bool operator==(const IntWrapper& x, const IntWrapperLoose& y) + { + return x.v == y.v; + } + + bool operator==(INT val) const + { + return this->v == val; + } + + friend bool operator==(INT val, const IntWrapperLoose& x) + { + return val == x.v; + } + + operator INT() const + { + return this->v; + } +}; + + +typedef IntWrapper SRTSOCKET; +typedef IntWrapper SRTSTATUS; +typedef IntWrapper SRTRUNSTATUS; +typedef IntWrapperLoose SRTSTATUS_LOOSE; + + diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 21a49e393..447ac4f99 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -226,6 +226,33 @@ See the full list in [Rejection Reason Codes](./rejection-codes.md). | | | +## Diagnostics and return types + +The SRT API functions usually report a status of the operation that they attempt to perform. +There are three general possibilities to report a success or failure, possibly with some +extra information: + +1. `SRTSTATUS` is usually an integer value with two possible variants: + * `SRT_STATUS_OK` (value: 0): the operation completed successfully + * `SRT_ERROR` (value: -1): the operation failed + +2. `SRTSOCKET` can be returned by some of the functions, which can be: + * A positive value greater than 0, which is a valid Socket ID value + * `SRT_SOCKID_CONNREQ` for a success report when a Socket ID needs not be returned + * `SRT_INVALID_SOCK` for a failure report + +3. A value of type `int` that should be a positive value or 0 in case of a success, +and the value equal to `SRT_ERROR` (that is, -1) in case of failure. + +In the below function description, functions returning `SRTSTATUS` will not +have the provided return value description, as it always maches the one above. +For all other types the function-specific return value description will be provided. + +If the function returns `SRT_ERROR`, `SRT_INVALID_SOCK` or a value equal to -1 +in case of returning an `int` value, additional error code can be obtained +through the [`srt_getlasterror`](#srt_getlasterror) call. Possible codes for a +particular function are listed in the **Errors** table. + ## Library Initialization @@ -235,7 +262,7 @@ See the full list in [Rejection Reason Codes](./rejection-codes.md). ### srt_startup ``` -int srt_startup(void); +SRTRUNSTATUS srt_startup(void); ``` This function shall be called at the start of an application that uses the SRT @@ -246,10 +273,10 @@ relying on this behavior is strongly discouraged. | Returns | | |:----------------------------- |:--------------------------------------------------------------- | -| 0 | Successfully run, or already started | -| 1 | This is the first startup, but the GC thread is already running | -| -1 | Failed | -| | | +| `SRT_RUN_OK` (0) | Successfully started | +| `SRT_RUN_ALREADY` (1) | The GC thread is already running or it was called once already | +| `SRT_RUN_ERROR` (-1) | Failed | +| | | | Errors | | |:----------------------------- |:--------------------------------------------------------------- | @@ -263,7 +290,7 @@ relying on this behavior is strongly discouraged. ### srt_cleanup ``` -int srt_cleanup(void); +SRTSTATUS srt_cleanup(void); ``` This function cleans up all global SRT resources and shall be called just before @@ -271,10 +298,8 @@ exiting the application that uses the SRT library. This cleanup function will st be called from the C++ global destructor, if not called by the application, although relying on this behavior is strongly discouraged. -| Returns | | -|:----------------------------- |:--------------------------------------------------------------- | -| 0 | A possibility to return other values is reserved for future use | -| | | +Currently this function can only return `SRT_STATUS_OK` and a possibility to return +`SRT_ERROR` is reserved for future use. **IMPORTANT**: Note that the startup/cleanup calls have an instance counter. This means that if you call [`srt_startup`](#srt_startup) multiple times, you need to call the @@ -340,11 +365,11 @@ Note that socket IDs always have the `SRTGROUP_MASK` bit clear. |:----------------------------- |:------------------------------------------------------- | | Socket ID | A valid socket ID on success | | `SRT_INVALID_SOCK` | (`-1`) on error | -| | | +| | | -| Errors | | -|:----------------------------- |:------------------------------------------------------------ | -| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources . | +| Errors | | +|:----------------------------- |:-------------------------------------------------- | +| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources | | | | **NOTE:** This is probably a design flaw (:warning:   **BUG?**). Usually underlying system @@ -358,7 +383,7 @@ errors are reported by [`SRT_ECONNSETUP`](#srt_econnsetup). ### srt_bind ``` -int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Binds a socket to a local address and port. Binding specifies the local network @@ -366,10 +391,11 @@ interface and the UDP port number to be used for the socket. When the local address is a wildcard (`INADDR_ANY` for IPv4 or `in6addr_any` for IPv6), then it's bound to all interfaces. -**IMPORTANT**: When you bind an IPv6 wildcard address, note that the -`SRTO_IPV6ONLY` option must be set on the socket explicitly to 1 or 0 prior to -calling this function. See -[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) for more details. +**IMPORTANT**: In the case of IPv6 wildcard address, this may mean either "all +IPv6 interfaces" or "all IPv4 and IPv6 interfaces", depending on the value of +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option. Therefore this +option must be explicitly set to 0 or 1 prior to calling this function, otherwise +(when the default -1 value of this option is left) this function will fail. Binding is necessary for every socket to be used for communication. If the socket is to be used to initiate a connection to a listener socket, which can be done, @@ -416,7 +442,7 @@ binding ("shared binding") is possessed by an SRT socket created in the same application, and: * Its binding address and UDP-related socket options match the socket to be bound. -* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to *true* (default). +* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDR) is set to *true* (default). If none of the free, side and shared binding options is currently possible, this function will fail. If the socket blocking the requested endpoint is an SRT @@ -424,14 +450,15 @@ socket in the current application, it will report the `SRT_EBINDCONFLICT` error, while if it was another socket in the system, or the problem was in the system in general, it will report `SRT_ESOCKFAIL`. Here is the table that shows possible situations: -| Requested binding | vs. Existing bindings... | | | | | -|---------------------|------------------------------|-----------|-----------------------------|---------------|---------------| -| | A.B.C.D | 0.0.0.0 | ::X | :: / V6ONLY=1 | :: / V6ONLY=0 | -| 1.2.3.4 | 1.2.3.4 shareable, else free | blocked | free | free | blocked | -| 0.0.0.0 | blocked | shareable | free | free | blocked | -| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | -| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | -| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | +| Requested binding | vs. Existing bindings... | | | | | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| | A.B.C.D (explicit IPv4 addr.) | 0.0.0.0 | ::X (explicit IPv6 addr.) | :: / V6ONLY=1 | :: / V6ONLY=0 | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| 1.2.3.4 | shareable if 1.2.3.4, else free | blocked | free | free | blocked | +| 0.0.0.0 | blocked | shareable | free | free | blocked | +| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | +| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | +| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | Where: @@ -441,7 +468,7 @@ Where: * shareable: This binding can be shared with the requested binding if it's compatible. -* (ADDRESS) shareable, else free: this binding is shareable if the existing binding address is +* shareable if (ADDRESS), else free: this binding is shareable if the existing binding address is equal to the requested ADDRESS. Otherwise it's free. If the binding is shareable, then the operation will succeed if the socket that currently @@ -460,13 +487,9 @@ or set the appropriate source address using **IMPORTANT information about IPv6**: If you are going to bind to the `in6addr_any` IPv6 wildcard address (known as `::`), the `SRTO_IPV6ONLY` option must be first set explicitly to 0 or 1, otherwise the binding -will fail. In all other cases this option is meaningless. See `SRTO_IPV6ONLY` -option for more information. - -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) on error, otherwise 0 | -| | | +will fail. In all other cases this option is meaningless. See +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option for more +information. | Errors | | |:---------------------------------------- |:-------------------------------------------------------------------- | @@ -486,7 +509,7 @@ option for more information. ### srt_bind_acquire ``` -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); ``` A version of [`srt_bind`](#srt_bind) that acquires a given UDP socket instead of creating one. @@ -529,7 +552,7 @@ Gets the current status of the socket. Possible states are: ### srt_getsndbuffer ``` -int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); +SRTSTATUS srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); ``` Retrieves information about the sender buffer. @@ -603,18 +626,13 @@ With default options this value should be 1456 for IPv4 and 1444 for IPv6. ### srt_close ``` -int srt_close(SRTSOCKET u); +SRTSTATUS srt_close(SRTSOCKET u); ``` Closes the socket or group and frees all used resources. Note that underlying UDP sockets may be shared between sockets, so these are freed only with the last user closed. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------- |:----------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -642,7 +660,7 @@ last user closed. ### srt_listen ``` -int srt_listen(SRTSOCKET u, int backlog); +SRTSTATUS srt_listen(SRTSOCKET u, int backlog); ``` This sets up the listening state on a socket with a backlog setting that @@ -657,11 +675,6 @@ be called before [`srt_accept`](#srt_accept) can happen * [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) option allows the listener socket to accept group connections -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0. | -| | | - | Errors | | |:--------------------------------------- |:-------------------------------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | Value of `backlog` is 0 or negative. | @@ -846,7 +859,7 @@ calling this function. | Returns | | |:----------------------------- |:---------------------------------------------------------------------- | -| SRT socket
group ID | On success, a valid SRT socket or group ID to be used for transmission | +| SRT socket/group ID | On success, a valid SRT socket or group ID to be used for transmission | | `SRT_INVALID_SOCK` | (-1) on failure | | | | @@ -866,7 +879,7 @@ calling this function. ### srt_listen_callback ``` -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a socket that is @@ -883,12 +896,6 @@ i.e. before `srt_listen` is called. * `hook_fn`: The callback hook function pointer (or NULL to remove the callback) * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:---------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - | Errors | | |:--------------------------------- |:----------------------------------------- | | [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| @@ -954,7 +961,7 @@ database you have to check against the data received in `streamid` or `peeraddr` ### srt_connect ``` -int srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Connects a socket or a group to a remote party with a specified address and port. @@ -972,8 +979,8 @@ Connects a socket or a group to a remote party with a specified address and port 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). +If not used, the binding will be done automatically to a wildcard address and port 0. See +[`srt_bind](#srt_bind) for details. 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 @@ -989,16 +996,21 @@ automatically for every call of this function. 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. +The returned value is a socket ID value. When `u` is a socket ID, the returned +is a special value `SRT_SOCKID_CONNREQ`. When `u` is a group ID, the returned +value is the socket ID of the newly created member for the requested link. In +the case of failure, `SRT_INVALID_SOCK` is returned. + | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | | Errors | | |:------------------------------------- |:----------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket or group ID | | [`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 | @@ -1023,7 +1035,9 @@ In the case of "late" failures you can additionally call 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. +can also be obtained from this function. Note that the connection timeout +error can be also recognized through this call, even though it is reported +by `SRT_ENOSERVER` in the blocking mode. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1033,7 +1047,7 @@ can also be obtained from this function. ### srt_connect_bind ``` -int srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); ``` @@ -1051,8 +1065,8 @@ first on the automatically created socket for the connection. | Returns | | |:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | @@ -1081,7 +1095,7 @@ different families (that is, both `source` and `target` must be `AF_INET` or ### srt_connect_debug ``` -int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); ``` This function is for developers only and can be used for testing. It does the @@ -1096,7 +1110,7 @@ is generated randomly. ### srt_rendezvous ``` -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); ``` Performs a rendezvous connection. This is a shortcut for doing bind locally, @@ -1109,11 +1123,6 @@ to true, and doing [`srt_connect`](#srt_connect). * `local_name`: specifies the local network interface and port to bind * `remote_name`: specifies the remote party's IP address and port -| Returns | | -|:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------------- |:-------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | @@ -1135,7 +1144,7 @@ allowed (that is, both `local_name` and `remote_name` must be `AF_INET` or `AF_I ### srt_connect_callback ``` -int srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a given [`u`](#u) @@ -1167,16 +1176,10 @@ connection failures. * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - -| Errors | | -|:---------------------------------- |:------------------------------------------| -| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| -| | | +| Errors | | +|:---------------------------------- |:-------------------------------------------| +| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket | +| | | The callback function signature has the following type definition: @@ -1248,7 +1251,7 @@ where: * `token`: An integer value unique for every connection, or -1 if unused The `srt_prepare_endpoint` sets these fields to default values. After that -you can change the value of `weight` and `config` and `token` fields. The +you can change the value of `weight`, `config` and `token` fields. The `weight` parameter's meaning is dependent on the group type: * BROADCAST: not used @@ -1392,10 +1395,12 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRTSOCKET` | Corresponding group SRT socket ID of the member socket. | +| `SRTSOCKET` | Corresponding group SRT socket ID of the `member` socket. | | `SRT_INVALID_SOCK` | The socket doesn't exist, it is not a member of any group, or bonding API is disabled. | | | | +In the case of `SRT_INVALID_SOCK`, the error is set to `SRT_EINVPARAM`. + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1404,7 +1409,7 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb #### srt_group_data ``` -int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); +SRTSTATUS srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); ``` **Arguments**: @@ -1415,23 +1420,26 @@ int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* in and is set to the filled array's size This function obtains the current member state of the group specified in -`socketgroup`. The `output` should point to an array large enough to hold all -the elements. The `inoutlen` should point to a variable initially set to the size -of the `output` array. The current number of members will be written back to `inoutlen`. +`socketgroup`. -If the size of the `output` array is enough for the current number of members, -the `output` array will be filled with group data and the function will return -the number of elements filled. Otherwise the array will not be filled and -`SRT_ERROR` will be returned. +The `inoutlen` should point to a variable initially set to the size +of the `output` array. The current number of members will be written back to +the variable specified in `inoutlen`. This paramterer cannot be NULL. -This function can be used to get the group size by setting `output` to `NULL`, -and providing `socketgroup` and `inoutlen`. +If `output` is specified and the size of the array is at least equal to the +number of group members, the `output` array will be filled with group data. -| Returns | | -|:----------------------------- |:-------------------------------------------------- | -| # of elements | The number of data elements filled, on success | -| -1 | Error | -| | | +If `output` is NULL then the function will only retrieve the number of elements +in `inoutlen`. + +This call will fail and return `SRT_ERROR` if: + +* The `socketgroup` parameter is invalid + +* The `inoutlen` parameter is NULL + +* The size specified in a variable passed via `inoutlen` is less than the number +of group members | Errors | | @@ -1441,13 +1449,13 @@ and providing `socketgroup` and `inoutlen`. | | | -| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | -|:---------:|:--------------:|:------------:|:----------:|:------------:|:---------------------------------:| -| NULL | NULL | -1 | NULL | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| NULL | ptr | 0 | NULL | group.size() | ✖️ | -| ptr | NULL | -1 | ✖️ | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| ptr | ≥ group.size | group.size() | group.data | group.size | ✖️ | -| ptr | < group.size | -1 | ✖️ | group.size | [`SRT_ELARGEMSG`](#srt_elargemsg) | +| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | +|:---------:|:--------------:|:---------------:|:----------:|:------------:|:---------------------------------:| +| ptr | ≥ group.size | `SRT_STATUS_OK` | group.data | group.size() | ✖️ | +| NULL | ptr | `SRT_STATUS_OK` | (unused) | group.size() | ✖️ | +| NULL | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | < group.size | `SRT_ERROR` | (unused) | group.size() | [`SRT_ELARGEMSG`](#srt_elargemsg) | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1457,14 +1465,14 @@ and providing `socketgroup` and `inoutlen`. #### srt_connect_group ``` -int srt_connect_group(SRTSOCKET group, - SRT_SOCKGROUPCONFIG name [], int arraysize); +SRTSOCKET srt_connect_group(SRTSOCKET group, + SRT_SOCKGROUPCONFIG links [], int arraysize); ``` This function does almost the same as calling [`srt_connect`](#srt_connect) or [`srt_connect_bind`](#srt_connect_bind) (when the source was specified for [`srt_prepare_endpoint`](#srt_prepare_endpoint)) in a loop for every item specified -in the `name` array. However if blocking mode is being used, the first call to +in the `links` array. However if blocking mode is being used, 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. @@ -1473,21 +1481,21 @@ option), there's no difference, except that the [`SRT_SOCKGROUPCONFIG`](#SRT_SOC structure allows adding extra configuration data used by groups. Note also that this function accepts only groups, not sockets. -The elements of the `name` array need to be prepared with the use of the +The elements of the `links` array need to be prepared with the use of the [`srt_prepare_endpoint`](#srt_prepare_endpoint) function. Note that it is **NOT** required that every target address specified is of the same family. Return value and errors in this function are the same as in [`srt_connect`](#srt_connect), although this function reports success when at least one connection has succeeded. If none has succeeded, this function reports an [`SRT_ECONNLOST`](#srt_econnlost) -error. Particular connection states can be obtained from the `name` -array upon return from the [`errorcode`](#error-codes) field. +error. Particular connection states can be obtained from the `links` array upon +return from the [`errorcode`](#error-codes) field. The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the following meaning: **Input**: -* `id`: unused, should be -1 (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) +* `id`: unused, should be `SRT_INVALID_SOCK` (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) * `srcaddr`: address to bind before connecting, if specified (see below for details) * `peeraddr`: target address to connect * `weight`: weight value to be set on the link @@ -1497,7 +1505,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f **Output**: -* `id`: The socket created for that connection (-1 if failed to create) +* `id`: The socket created for that connection (`SRT_INVALID_SOCK` if failed to create) * `srcaddr`: unchanged * `peeraddr`: unchanged * `weight`: unchanged @@ -1507,8 +1515,8 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f | Returns | | |:----------------------------- |:-------------------------------------------------- | -| `SRT_SOCKET` | The socket ID of the first connected member. | -| -1 | Error | +| Socket ID | The socket ID of the first connected member. | +| `SRT_INVALID_SOCK` | Error | | | | @@ -1519,7 +1527,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f | | | The procedure of connecting for every connection definition specified -in the `name` array is performed the following way: +in the `links` array is performed the following way: 1. The socket for this connection is first created @@ -1546,7 +1554,8 @@ then, and for which the connection attempt has at least successfully started, remain group members, although the function will return immediately with an error status (that is, without waiting for the first successful connection). If your application wants to do any partial recovery from this situation, it can -only use the epoll mechanism to wait for readiness. +only check the current member status via [`srt_group_data`](#srt_group_data) +and wait for group's write readiness (`SRT_EPOLL_OUT`) by using epoll. 2. In any other case, if an error occurs at any stage of the above process, the processing is interrupted for this very array item only, the socket used for it @@ -1554,16 +1563,18 @@ is immediately closed, and the processing of the next elements continues. In the of a connection process, it also passes two stages - parameter check and the process itself. Failure at the parameter check breaks this process, while if the check passes, this item is considered correctly processed, even if the connection -attempt is going to fail later. If this function is called in blocking mode, -it then blocks until at least one connection reports success, or if all of them -fail. The status of connections that continue in the background after this function -exits can then be checked by [`srt_group_data`](#srt_group_data). +attempt is going to fail later. + +If this function is called in blocking mode, it then blocks until at least one +connection reports success, or if all of them fail. The status of connections +that continue in the background after this function exits can then be checked +by [`srt_group_data`](#srt_group_data). As member socket connections are running in the background, for determining if a particular connection has succeeded or failed it is recommended to use [`srt_connect_callback`](#srt_connect_callback). In this case the `token` callback function parameter will be the same as the `token` value used -for the particular item in the `name` connection table. +for the particular item in the `links` connection array. The `token` value doesn't have any limitations except that the -1 value is a "trap representation", that is, when set on input it will make the internals @@ -1665,7 +1676,7 @@ Deletes the configuration object. #### srt_config_add ``` -int srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); ``` Adds a configuration option to the configuration object. @@ -1695,12 +1706,6 @@ The following options are allowed to be set on the member socket: * [`SRTO_UDP_SNDBUF`](API-socket-options.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:---------------------------------- |:--------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | This option is not allowed to be set on a socket being a group member. Or if bonding API is disabled. | @@ -1726,15 +1731,11 @@ The following options are allowed to be set on the member socket: ### srt_getpeername ``` -int srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Retrieves the remote address to which the socket is connected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:------------------------------------------------------------------------ | @@ -1749,7 +1750,7 @@ Retrieves the remote address to which the socket is connected. ### srt_getsockname ``` -int srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Extracts the address to which the socket was bound. Although you should know @@ -1758,10 +1759,6 @@ useful for extracting the local outgoing port number when it was specified as 0 with binding for system autoselection. With this function you can extract the port number after it has been autoselected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:---------------------------------------------- | @@ -1790,8 +1787,8 @@ if (res < 0) { ### 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); +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); +SRTSTATUS 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). @@ -1809,11 +1806,6 @@ For most options, it will be the size of an integer. Some options, however, use The application is responsible for allocating sufficient memory space as defined and pointed to by `optval`. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:-------------------------------- |:---------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1828,8 +1820,8 @@ The application is responsible for allocating sufficient memory space as defined ### 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); +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); ``` Sets a value for a socket option in the socket or group. @@ -1846,11 +1838,6 @@ Please note that some of the options can only be set on sockets or only on groups, although most of the options can be set on the groups so that they are then derived by the member sockets. -| Returns | | -|:----------------------------- |:----------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:----------------------------------- |:--------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1859,7 +1846,7 @@ are then derived by the member sockets. | [`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 +**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). @@ -1874,7 +1861,7 @@ uint32_t srt_getversion(); ``` Get SRT version value. The version format in hex is 0xXXYYZZ for x.y.z in human -readable form, where x = ("%d", (version>>16) & 0xff), etc. +readable form. E.g. 0x012033 means version 1.20.33. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -2042,7 +2029,7 @@ single call to this function determines a message's boundaries. |:----------------------------- |:--------------------------------------------------------- | | Size | Size of the data sent, if successful | | `SRT_ERROR` | In case of error (-1) | -| | | +| | | **NOTE**: Note that in **file/stream mode** the returned size may be less than `len`, which means that it didn't send the whole contents of the buffer. You would need to @@ -2121,10 +2108,10 @@ the currently lost one, it will be delivered and the lost one dropped. | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | Size (\>0) of the data received, if successful. | +| Size value \> 0 | Size of the data received, if successful. | | 0 | If the connection has been closed | | `SRT_ERROR` | (-1) when an error occurs | -| | | +| | | | Errors | | |:--------------------------------------------- |:--------------------------------------------------------- | @@ -2177,7 +2164,7 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | The size (\>0) of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | +| Size value \> 0 | The size of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | | -1 | in case of error | | | | @@ -2221,10 +2208,10 @@ the required range already, so for a numbers like 0x7FFFFFF0 and 0x10, for which ### srt_bistats ``` // Performance monitor with Byte counters for better bitrate estimation. -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); ``` Reports the current statistics @@ -2240,12 +2227,6 @@ Reports the current statistics of the fields please refer to [SRT Statistics](statistics.md). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Invalid socket ID provided. @@ -2298,11 +2279,11 @@ function can then be used to block until any readiness status in the whole int srt_epoll_create(void); ``` -Creates a new epoll container. +Creates a new epoll container and returns its identifier (EID). | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| valid EID | Success | +| valid EID >= 0 | Success | | -1 | Failure | | | | @@ -2322,10 +2303,10 @@ Creates a new epoll container. ### srt_epoll_update_ssock ``` -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); ``` Adds a socket to a container, or updates an existing socket subscription. @@ -2405,12 +2386,6 @@ as level-triggered, you can do two separate subscriptions for the same socket. any possible flag, you must use [`srt_epoll_uwait`](#srt_epoll_uwait). Note that this function doesn't work with system file descriptors. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2429,8 +2404,8 @@ the [`SRT_ECONNSETUP`](#srt_econnsetup) code is predicted. ### srt_epoll_remove_ssock ``` -int srt_epoll_remove_usock(int eid, SRTSOCKET u); -int srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); ``` Removes a specified socket from an epoll container and clears all readiness @@ -2439,12 +2414,6 @@ states recorded for that socket. The `_usock` suffix refers to a user socket (SRT socket). The `_ssock` suffix refers to a system socket. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2503,7 +2472,7 @@ the only way to know what kind of error has occurred on the socket. | Returns | | |:----------------------------- |:------------------------------------------------------------ | -| Number | The number (\>0) of ready sockets, of whatever kind (if any) | +| Number \> 0 | The number of ready sockets, of whatever kind (if any) | | -1 | Error | | | | @@ -2542,7 +2511,7 @@ indefinitely until a readiness state occurs. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------- | -| Number | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | +| Number \> 0 | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | | `fdsSize` + 1 | This means that there was not enough space in the output array to report all events.
For events subscribed with the [`SRT_EPOLL_ET`](#SRT_EPOLL_ET) flag only those will be cleared that were reported.
Others will wait for the next call. | | 0 | If no readiness state was found on any socket and the timeout has passed
(this is not possible when waiting indefinitely) | | -1 | Error | @@ -2551,8 +2520,14 @@ indefinitely until a readiness state occurs. | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | -| [`SRT_EINVPARAM`](#srt_einvparam) | One of possible usage errors:
* `fdsSize` is < 0
* `fdsSize` is > 0 and `fdsSet` is a null pointer
* [`eid`](#eid) was subscribed to any system socket | -| | | +| [`SRT_EINVPARAM`](#srt_einvparam) | Usage error (see below) | +| | | + +Usage errors reported as `SRT_EINVPARAM`: + +* `fdsSize` is \< 0 +* `fdsSize` is \> 0 and `fdsSet` is a null pointer +* [`eid`](#eid) was subscribed to any system socket **IMPORTANT**: This function reports timeout by returning 0, not by [`SRT_ETIMEOUT`](#srt_etimeout) error. @@ -2582,18 +2557,12 @@ closed and its state can be verified with a call to [`srt_getsockstate`](#srt_ge ### srt_epoll_clear_usocks ``` -int srt_epoll_clear_usocks(int eid); +SRTSTATUS srt_epoll_clear_usocks(int eid); ``` This function removes all SRT ("user") socket subscriptions from the epoll container identified by [`eid`](#eid). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2635,7 +2604,7 @@ 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 | +| `SRT_ERROR` (-1) | Special value in case when an error occurred | | | | | Errors | | @@ -2650,17 +2619,11 @@ the general output array is not empty. ### srt_epoll_release ``` -int srt_epoll_release(int eid); +SRTSTATUS srt_epoll_release(int eid); ``` Deletes the epoll container. -| Returns | | -|:----------------------------- |:-------------------------------------------------------------- | -| | The number (\>0) of ready sockets, of whatever kind (if any) | -| -1 | Error | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2876,7 +2839,7 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistic | Returns | | |:----------------------------- |:--------------------------------------------------------------------------- | | | Connection time in microseconds elapsed since epoch of SRT internal clock | -| -1 | Error | +| `SRT_ERROR` (-1) | Error | | | | | Errors | | @@ -3028,7 +2991,7 @@ The actual messages assigned to the internal rejection codes, that is, less than ### srt_setrejectreason ``` -int srt_setrejectreason(SRTSOCKET sock, int value); +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); ``` Sets the rejection code on the socket. This call is only useful in the listener @@ -3042,12 +3005,6 @@ can inform the calling side that the resource specified under the `r` key in the 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 | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Error | -| -1 | Success | -| | | - | Errors | | |:--------------------------------- |:-------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket `sock` is not an ID of a valid socket | @@ -3073,6 +3030,8 @@ used for the connection, the function should also be called when the a numeric code, which can be translated into a message by [`srt_rejectreason_str`](#srt_rejectreason_str). +The returned value is one of the values listed in enum `SRT_REJECT_REASON`. +For an invalid value of `sock` the `SRT_REJ_UNKNOWN` is returned. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -3083,7 +3042,8 @@ a numeric code, which can be translated into a message by #### SRT_REJ_UNKNOWN -A fallback value for cases when there was no connection rejected. +A fallback value for cases when there was no connection rejected or the +reason cannot be obtained. #### SRT_REJ_SYSTEM @@ -3285,7 +3245,8 @@ is no longer usable. #### SRT_ECONNFAIL -General connection failure of unknown details. +General connection failure of unknown details (currently is not reported +directly by any API function and it's reserved for future use). #### SRT_ECONNLOST @@ -3503,30 +3464,32 @@ Example 1: * Socket 1: bind to IPv4 0.0.0.0 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = true -* Result: NOT intersecting +* Result: NOT intersecting, allowed to proceed Example 2: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv4 0.0.0.0 -* Result: intersecting (and conflicting) +* Result: failure: 0.0.0.0 encloses 1.2.3.4, so they are in conflict Example 3: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = false -* Result: intersecting (and conflicting) +* Result: failure: this encloses all IPv4, so it conflicts with 1.2.3.4 -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: +Binding another socket to an endpoint that is already bound by another +socket is possible, and results in a shared binding, as long as the binding +address that is enclosed by this existing binding is exactly identical to +the specified one and 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. +3. The UDP-specific settings (SRT options that map to UDP options) must be identical. If any of these conditions isn't satisfied, the `srt_bind` function results in conflict and report this error. diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp index af551849b..ce80564d9 100644 --- a/examples/sendmsg.cpp +++ b/examples/sendmsg.cpp @@ -100,7 +100,7 @@ int main(int argc, char* argv[]) // 2. Otherwise the first number is the ID, followed by a space, to be filled in first 4 bytes. // 3. Rest of the characters, up to the end of line, should be put into a solid block and sent at once. - int status = 0; + int status = SRT_STATUS_OK; int ordinal = 1; int lpos = 0; diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a7f362bb8..ede03aad5 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -239,11 +239,11 @@ srt::CUDTUnited::~CUDTUnited() string srt::CUDTUnited::CONID(SRTSOCKET sock) { - if (sock == 0) + if (int32_t(sock) <= 0) // embraces SRT_INVALID_SOCK, SRT_SOCKID_CONNREQ and illegal negative domain return ""; std::ostringstream os; - os << "@" << sock << ":"; + os << "@" << int(sock) << ":"; return os.str(); } @@ -299,18 +299,21 @@ void srt::CUDTUnited::closeAllSockets() #endif 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()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } + // 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); + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); + } } m_Sockets.clear(); @@ -340,17 +343,17 @@ void srt::CUDTUnited::closeAllSockets() } -int srt::CUDTUnited::startup() +SRTRUNSTATUS srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); m_iInstanceCount++; if (m_bGCStatus) - return (m_iInstanceCount == 1) ? 1 : 0; + return (m_iInstanceCount == 1) ? SRT_RUN_ALREADY : SRT_RUN_OK; else - return startGarbageCollector() ? 0 : -1; + return startGarbageCollector() ? SRT_RUN_OK : SRT_RUN_ERROR; } -int srt::CUDTUnited::cleanup() +SRTSTATUS srt::CUDTUnited::cleanup() { // IMPORTANT!!! // In this function there must be NO LOGGING AT ALL. This function may @@ -363,11 +366,11 @@ int srt::CUDTUnited::cleanup() ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) - return 0; + return SRT_STATUS_OK; stopGarbageCollector(); closeAllSockets(); - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) @@ -418,10 +421,10 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) const bool exists = #if ENABLE_BONDING for_group - ? m_Groups.count(sockval | SRTGROUP_MASK) + ? m_Groups.count(SRTSOCKET(sockval | SRTGROUP_MASK)) : #endif - m_Sockets.count(sockval); + m_Sockets.count(SRTSOCKET(sockval)); leaveCS(m_GlobControlLock); if (exists) @@ -470,7 +473,7 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) LOGC(smlog.Debug, log << "generateSocketID: " << (for_group ? "(group)" : "") << ": @" << sockval); - return sockval; + return SRTSOCKET(sockval); } SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) @@ -500,7 +503,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) throw; } ns->m_Status = SRTS_INIT; - ns->m_ListenSocket = 0; + ns->m_ListenSocket = SRT_SOCKID_CONNREQ; // A value used for socket if it wasn't listener-spawned ns->core().m_SocketID = ns->m_SocketID; ns->core().m_pCache = m_pCache; @@ -527,7 +530,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } -int srt::CUDTUnited::newConnection(const SRTSOCKET listen, +int srt::CUDTUnited::newConnection(const SRTSOCKET listener, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs, @@ -541,15 +544,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // Can't manage this error through an exception because this is // running in the listener loop. - CUDTSocket* ls = locateSocket(listen); + CUDTSocket* ls = locateSocket(listener); if (!ls) { - LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listen << " which DOES NOT EXIST."); + LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listener << " which DOES NOT EXIST."); return -1; } HLOGC(cnlog.Debug, - log << "newConnection: creating new socket after listener @" << listen + log << "newConnection: creating new socket after listener @" << listener << " contacted with backlog=" << ls->m_uiBackLog); // if this connection has already been processed @@ -631,13 +634,13 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return -1; } - ns->m_ListenSocket = listen; + ns->m_ListenSocket = listener; 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->core().m_SocketID + log << "newConnection: DATA: lsnid=" << listener << " id=" << ns->core().m_SocketID << " peerid=" << ns->core().m_PeerID << " ISN=" << ns->m_iISN); int error = 0; @@ -718,7 +721,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, << " to that socket (" << ns->m_SocketID << ")"); m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); - LOGC(cnlog.Note, log << "@" << ns->m_SocketID << " connection on listener @" << listen + LOGC(cnlog.Note, log << "@" << ns->m_SocketID << " connection on listener @" << listener << " (" << ns->m_SelfAddr.str() << ") from peer @" << ns->m_PeerID << " (" << peer.str() << ")"); } catch (...) @@ -805,7 +808,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, 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->core().m_sPollID, SRT_EPOLL_ACCEPT, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); CGlobEvent::triggerEvent(); @@ -826,7 +829,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // 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->core().m_sPollID, SRT_EPOLL_UPDATE, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); #if ENABLE_BONDING // Note that the code in this current IF branch can only be executed in case // of group members. Otherwise should_submit_to_accept will be always true. @@ -948,12 +951,12 @@ int srt::CUDTUnited::checkQueuedSocketsEvents(const map #endif // static forwarder -int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return uglobal().installAcceptHook(lsn, hook, opaq); } -int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { try { @@ -966,24 +969,24 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } -int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return uglobal().installConnectHook(lsn, hook, opaq); } -int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) { try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->installConnectHook(hook, opaq); - return 0; + return SRT_STATUS_OK; } #endif CUDTSocket* s = locateSocket(u, ERH_THROW); @@ -995,7 +998,7 @@ int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) @@ -1015,7 +1018,7 @@ SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } -int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -1039,10 +1042,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) { ScopedLock cg(s->m_ControlLock); @@ -1068,10 +1071,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr(s->m_SelfAddr); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) { if (backlog <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -1094,7 +1097,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // do nothing if the socket is already listening if (s->m_Status == SRTS_LISTENING) - return 0; + return SRT_STATUS_OK; // a socket can listen only if is in OPENED status if (s->m_Status != SRTS_OPENED) @@ -1114,7 +1117,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // if thrown, remains in OPENED state if so. s->m_Status = SRTS_LISTENING; - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut) @@ -1156,7 +1159,7 @@ SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, i // Theoretically we can have a situation that more than one // listener is ready for accept. In this case simply get // only the first found. - int lsn = st.begin()->first; + SRTSOCKET lsn = st.begin()->first; sockaddr_storage dummy; int outlen = sizeof dummy; return accept(lsn, ((sockaddr*)&dummy), (&outlen)); @@ -1196,7 +1199,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); } - SRTSOCKET u = CUDT::INVALID_SOCK; + SRTSOCKET u = SRT_INVALID_SOCK; bool accepted = false; // !!only one connection can be set up each time!! @@ -1243,7 +1246,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, false); } - if (u == CUDT::INVALID_SOCK) + if (u == SRT_INVALID_SOCK) { // non-blocking receiving, no connection available if (!ls->core().m_config.bSynRecving) @@ -1382,7 +1385,7 @@ void srt::CUDTUnited::removePendingForGroup(const CUDTGroup* g) #endif -int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) +SRTSOCKET srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified if (!srcname || !tarname || namelen < int(sizeof(sockaddr_in))) @@ -1404,7 +1407,7 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); // Note: forced_isn is ignored when connecting a group. @@ -1425,10 +1428,11 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // For a single socket, just do bind, then connect bind(s, source_addr); - return connectIn(s, target_addr, SRT_SEQNO_NONE); + connectIn(s, target_addr, SRT_SEQNO_NONE); + return SRT_SOCKID_CONNREQ; } -int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { if (!name || namelen < int(sizeof(sockaddr_in))) { @@ -1444,7 +1448,7 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); @@ -1461,31 +1465,29 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - return connectIn(s, target_addr, forced_isn); + connectIn(s, target_addr, forced_isn); + return SRT_SOCKID_CONNREQ; } #if ENABLE_BONDING -int srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) +SRTSOCKET srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) { - int gstat = groupConnect(pg, gd, 1); - if (gstat == -1) + SRTSOCKET gstat = groupConnect(pg, gd, 1); + if (gstat == SRT_INVALID_SOCK) { // We have only one element here, so refer to it. // Sanity check if (gd->errorcode == SRT_SUCCESS) gd->errorcode = SRT_EINVPARAM; - CodeMajor mj = CodeMajor(gd->errorcode / 1000); - CodeMinor mn = CodeMinor(gd->errorcode % 1000); - - return CUDT::APIError(mj, mn); + return CUDT::APIError(gd->errorcode), SRT_INVALID_SOCK; } return gstat; } // [[using assert(pg->m_iBusy > 0)]] -int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) +SRTSOCKET srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) { CUDTGroup& g = *pg; SRT_ASSERT(g.m_iBusy > 0); @@ -1527,7 +1529,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // This also should happen if ERR flag was set, as IN and OUT could be set, too. m_EPoll.update_events(g.id(), g.m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } - SRTSOCKET retval = -1; + SRTSOCKET retval = SRT_INVALID_SOCK; int eid = -1; int connect_modes = SRT_EPOLL_CONNECT | SRT_EPOLL_ERR; @@ -1674,7 +1676,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } else { - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; delete ns; m_Sockets.erase(sid); @@ -1736,7 +1738,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // to avoid locking more than one mutex at a time. erc_rloc = e.getErrorCode(); targets[tii].errorcode = e.getErrorCode(); - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(ns->m_SocketID, ns->core().m_sPollID); @@ -1752,7 +1754,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i { LOGC(aclog.Fatal, log << "groupConnect: IPE: UNKNOWN EXCEPTION from connectIn"); targets[tii].errorcode = SRT_ESYSOBJ; - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); // You won't be updating any EIDs anymore. @@ -1803,7 +1805,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // set BROKEN or PENDING. f->sndstate = SRT_GST_PENDING; f->rcvstate = SRT_GST_PENDING; - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -1851,7 +1853,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } } - if (retval == -1) + if (retval == SRT_INVALID_SOCK) { HLOGC(aclog.Debug, log << "groupConnect: none succeeded as background-spawn, exit with error"); block_new_opened = false; // Avoid executing further while loop @@ -1864,13 +1866,13 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i if (spawned.empty()) { // All were removed due to errors. - retval = -1; + retval = SRT_INVALID_SOCK; break; } HLOGC(aclog.Debug, log << "groupConnect: first connection, applying EPOLL WAITING."); int len = (int)spawned.size(); vector ready(spawned.size()); - const int estat = srt_epoll_wait(eid, + const int estat = srt_epoll_wait(eid, NULL, NULL, // IN/ACCEPT &ready[0], @@ -1882,7 +1884,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i NULL); // Sanity check. Shouldn't happen if subs are in sync with spawned. - if (estat == -1) + if (estat == int(SRT_ERROR)) { #if ENABLE_LOGGING CUDTException& x = CUDT::getlasterror(); @@ -1894,7 +1896,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } #endif HLOGC(aclog.Debug, log << "groupConnect: srt_epoll_wait failed - breaking the wait loop"); - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -2016,14 +2018,14 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // standing. Each one could, however, break by a different reason, // for example, one by timeout, another by wrong passphrase. Check // the `errorcode` field to determine the reaon for particular link. - if (retval == -1) + if (retval == SRT_INVALID_SOCK) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); return retval; } #endif -int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) +void 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: @@ -2085,19 +2087,17 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i s->m_Status = SRTS_OPENED; throw; } - - return 0; } -int srt::CUDTUnited::close(const SRTSOCKET u) +SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->close(); deleteGroup(k.group); - return 0; + return SRT_STATUS_OK; } #endif #if ENABLE_HEAVY_LOGGING @@ -2172,7 +2172,7 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int srt::CUDTUnited::close(CUDTSocket* s) +SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); @@ -2222,7 +2222,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if (s->m_Status == SRTS_LISTENING) { if (s->core().m_bBroken) - return 0; + return SRT_STATUS_OK; s->m_tsClosureTimeStamp = steady_clock::now(); s->core().m_bBroken = true; @@ -2272,7 +2272,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) { HLOGC(smlog.Debug, log << "@" << u << "U::close: NOT AN ACTIVE SOCKET, returning."); - return 0; + return SRT_STATUS_OK; } s = i->second; s->setClosed(); @@ -2373,7 +2373,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) */ CSync::notify_one_relaxed(m_GCStopCond); - return 0; + return SRT_STATUS_OK; } void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) @@ -2632,21 +2632,20 @@ int srt::CUDTUnited::epoll_create() return m_EPoll.create(); } -int srt::CUDTUnited::epoll_clear_usocks(int eid) +void srt::CUDTUnited::epoll_clear_usocks(int eid) { return m_EPoll.clear_usocks(eid); } -int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +void srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { - int ret = -1; #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); - ret = m_EPoll.update_usock(eid, u, events); + m_EPoll.update_usock(eid, u, events); k.group->addEPoll(eid); - return 0; + return; } #endif @@ -2657,39 +2656,36 @@ int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int CUDTSocket* s = locateSocket_LOCKED(u); if (s) { - ret = epoll_add_usock_INTERNAL(eid, s, events); + epoll_add_usock_INTERNAL(eid, s, events); } else { throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); } } - - return ret; } // NOTE: WILL LOCK (serially): // - CEPoll::m_EPollLock // - CUDT::m_RecvLock -int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) +void 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); + m_EPoll.update_usock(eid, s->m_SocketID, events); s->core().addEPoll(eid); - return ret; } -int srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } -int srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.update_ssock(eid, s, events); } template -int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) +void 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. @@ -2705,31 +2701,29 @@ int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) HLOGC(ealog.Debug, log << "epoll_remove_usock: CLEARING subscription on E" << eid << " of @" << ent->id()); int no_events = 0; - int ret = m_EPoll.update_usock(eid, ent->id(), &no_events); - - return ret; + m_EPoll.update_usock(eid, ent->id(), &no_events); } // Needed internal access! -int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) +void srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { return epoll_remove_entity(eid, &s->core()); } #if ENABLE_BONDING -int srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) +void srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) { return epoll_remove_entity(eid, g); } #endif -int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +void srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { CUDTSocket* s = 0; #if ENABLE_BONDING CUDTGroup* g = 0; - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); g = k.group; @@ -2749,7 +2743,7 @@ int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) return m_EPoll.update_usock(eid, u, &no_events); } -int srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +void srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } @@ -2764,7 +2758,7 @@ int32_t srt::CUDTUnited::epoll_set(int eid, int32_t flags) return m_EPoll.setflags(eid, flags); } -int srt::CUDTUnited::epoll_release(const int eid) +void srt::CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } @@ -2975,18 +2969,21 @@ void srt::CUDTUnited::checkBrokenSockets() 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()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } + // 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); + 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(); j != m_ClosedSockets.end(); ++j) @@ -3202,7 +3199,7 @@ void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int 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; + w_m.m_iID = int32_t(s->m_SocketID); } uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) @@ -3673,12 +3670,12 @@ void* srt::CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -int srt::CUDT::startup() +SRTRUNSTATUS srt::CUDT::startup() { return uglobal().startup(); } -int srt::CUDT::cleanup() +SRTSTATUS srt::CUDT::cleanup() { return uglobal().cleanup(); } @@ -3691,19 +3688,19 @@ SRTSOCKET srt::CUDT::socket() } catch (const CUDTException& e) { - SetThreadLocalError(e); - return INVALID_SOCK; + APIError a(e); + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { - SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + APIError a(MJ_SYSTEMRES, MN_MEMORY, 0); + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "socket: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + APIError a(MJ_UNKNOWN, MN_NONE, 0); + return SRT_INVALID_SOCK; } } @@ -3717,6 +3714,13 @@ srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) SetThreadLocalError(CUDTException(mj, mn, syserr)); } +srt::CUDT::APIError::APIError(int errorcode) +{ + CodeMajor mj = CodeMajor(errorcode / 1000); + CodeMinor mn = CodeMinor(errorcode % 1000); + SetThreadLocalError(CUDTException(mj, mn, 0)); +} + #if ENABLE_BONDING // This is an internal function; 'type' should be pre-checked if it has a correct value. // This doesn't have argument of GroupType due to header file conflicts. @@ -3744,14 +3748,12 @@ SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (...) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } - - return SRT_INVALID_SOCK; } // [[using locked(m_ControlLock)]] @@ -3793,14 +3795,14 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) ScopedLock glock(uglobal().m_GlobControlLock); CUDTSocket* s = uglobal().locateSocket_LOCKED(socket); if (!s || !s->m_GroupOf) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; return s->m_GroupOf->id(); } -int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) +SRTSTATUS srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { - if ((groupid & SRTGROUP_MASK) == 0 || !psize) + if (!CUDT::isgroup(groupid) || !psize) { return APIError(MJ_NOTSUP, MN_INVAL, 0); } @@ -3816,7 +3818,7 @@ int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* } #endif -int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) { try { @@ -3850,7 +3852,7 @@ int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) } } -int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { @@ -3875,7 +3877,7 @@ int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) } } -int srt::CUDT::listen(SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDT::listen(SRTSOCKET u, int backlog) { try { @@ -3905,18 +3907,18 @@ SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept_bond: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } @@ -3929,22 +3931,22 @@ SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) { try { @@ -3952,29 +3954,29 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #if ENABLE_BONDING -int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) +SRTSOCKET srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) { if (arraysize <= 0) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; - if ((grp & SRTGROUP_MASK) == 0) + if (!CUDT::isgroup(grp)) { // connectLinks accepts only GROUP id, not socket id. - return APIError(MJ_NOTSUP, MN_SIDINVAL, 0); + return APIError(MJ_NOTSUP, MN_SIDINVAL, 0), SRT_INVALID_SOCK; } try @@ -3984,21 +3986,21 @@ int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int ar } catch (CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #endif -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { try { @@ -4006,20 +4008,20 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t f } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } -int srt::CUDT::close(SRTSOCKET u) +SRTSTATUS srt::CUDT::close(SRTSOCKET u) { try { @@ -4036,12 +4038,12 @@ int srt::CUDT::close(SRTSOCKET u) } } -int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getpeername(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4054,12 +4056,12 @@ int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getsockname(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4072,12 +4074,12 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockdevname(SRTSOCKET u, char* name, size_t* namelen) +SRTSTATUS srt::CUDT::getsockdevname(SRTSOCKET u, char* name, size_t* namelen) { try { uglobal().getsockdevname(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4090,7 +4092,7 @@ int srt::CUDT::getsockdevname(SRTSOCKET u, char* name, size_t* namelen) } } -int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) +SRTSTATUS srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) { @@ -4100,17 +4102,17 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4123,7 +4125,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval } } -int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +SRTSTATUS srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { if (!optval || optlen < 0) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -4131,17 +4133,17 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4176,7 +4178,7 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); @@ -4187,16 +4189,16 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "sendmsg: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4220,7 +4222,7 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); @@ -4231,12 +4233,12 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "recvmsg: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4249,16 +4251,16 @@ int64_t srt::CUDT::sendfile(SRTSOCKET u, fstream& ifs, int64_t& offset, int64_t } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "sendfile: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4270,12 +4272,12 @@ int64_t srt::CUDT::recvfile(SRTSOCKET u, fstream& ofs, int64_t& offset, int64_t } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "recvfile: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4283,7 +4285,7 @@ int srt::CUDT::select(int, UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET { if ((!readfds) && (!writefds) && (!exceptfds)) { - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0).as(); } try @@ -4292,16 +4294,16 @@ int srt::CUDT::select(int, UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "select: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4313,7 +4315,7 @@ int srt::CUDT::selectEx(const vector& fds, { if ((!readfds) && (!writefds) && (!exceptfds)) { - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0).as(); } try @@ -4322,16 +4324,16 @@ int srt::CUDT::selectEx(const vector& fds, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "selectEx: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN); + return APIError(MJ_UNKNOWN).as(); } } @@ -4343,20 +4345,21 @@ int srt::CUDT::epoll_create() } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_create: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } -int srt::CUDT::epoll_clear_usocks(int eid) +SRTSTATUS srt::CUDT::epoll_clear_usocks(int eid) { try { - return uglobal().epoll_clear_usocks(eid); + uglobal().epoll_clear_usocks(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4370,11 +4373,12 @@ int srt::CUDT::epoll_clear_usocks(int eid) } } -int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4387,11 +4391,12 @@ int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* even } } -int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_add_ssock(eid, s, events); + uglobal().epoll_add_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4404,11 +4409,12 @@ int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* even } } -int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4422,11 +4428,12 @@ int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* e } } -int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_update_ssock(eid, s, events); + uglobal().epoll_update_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4440,11 +4447,12 @@ int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* e } } -int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +SRTSTATUS srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { - return uglobal().epoll_remove_usock(eid, u); + uglobal().epoll_remove_usock(eid, u); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4458,11 +4466,12 @@ int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) } } -int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +SRTSTATUS srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { - return uglobal().epoll_remove_ssock(eid, s); + uglobal().epoll_remove_ssock(eid, s); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4489,12 +4498,12 @@ int srt::CUDT::epoll_wait(const int eid, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_wait: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4506,12 +4515,12 @@ int srt::CUDT::epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_uwait: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } @@ -4523,20 +4532,21 @@ int32_t srt::CUDT::epoll_set(const int eid, int32_t flags) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e).as(); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_set: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0).as(); } } -int srt::CUDT::epoll_release(const int eid) +SRTSTATUS srt::CUDT::epoll_release(const int eid) { try { - return uglobal().epoll_release(eid); + uglobal().epoll_release(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4554,10 +4564,10 @@ srt::CUDTException& srt::CUDT::getlasterror() return GetThreadLocalError(); } -int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +SRTSTATUS srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (CUDT::isgroup(u)) return groupsockbstats(u, perf, clear); #endif @@ -4565,7 +4575,7 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta { CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.bstats(perf, clear, instantaneous); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4579,24 +4589,24 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta } #if ENABLE_BONDING -int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) +SRTSTATUS srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->bstatsSocket(perf, clear); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { SetThreadLocalError(e); - return ERROR; + return SRT_ERROR; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "bstats: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return ERROR; + return SRT_ERROR; } } #endif @@ -4635,7 +4645,7 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->getStatus(); @@ -4666,12 +4676,12 @@ int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) CUDTSocket* s = locateSocket(id); if (!s) { - return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL); + return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL).as(); } if (s->m_SelfAddr.family() == AF_UNSPEC) { - return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND); + return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND).as(); } int fam = s->m_SelfAddr.family(); @@ -4682,7 +4692,7 @@ int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) if (extra == -1) { LOGP(aclog.Error, errmsg); - return CUDT::APIError(MJ_NOTSUP, MN_INVAL); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL).as(); } // Prefer transfer IP version, if defined. This is defined after @@ -4702,27 +4712,27 @@ int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) namespace UDT { -int startup() +SRTRUNSTATUS startup() { return srt::CUDT::startup(); } -int cleanup() +SRTSTATUS cleanup() { return srt::CUDT::cleanup(); } -int bind(SRTSOCKET u, const struct sockaddr* name, int namelen) +SRTSTATUS bind(SRTSOCKET u, const struct sockaddr* name, int namelen) { return srt::CUDT::bind(u, name, namelen); } -int bind2(SRTSOCKET u, UDPSOCKET udpsock) +SRTSTATUS bind2(SRTSOCKET u, UDPSOCKET udpsock) { return srt::CUDT::bind(u, udpsock); } -int listen(SRTSOCKET u, int backlog) +SRTSTATUS listen(SRTSOCKET u, int backlog) { return srt::CUDT::listen(u, backlog); } @@ -4732,39 +4742,39 @@ SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen) return srt::CUDT::accept(u, addr, addrlen); } -int connect(SRTSOCKET u, const struct sockaddr* name, int namelen) +SRTSOCKET connect(SRTSOCKET u, const struct sockaddr* name, int namelen) { return srt::CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } -int close(SRTSOCKET u) +SRTSTATUS close(SRTSOCKET u) { return srt::CUDT::close(u); } -int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) +SRTSTATUS getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) { return srt::CUDT::getpeername(u, name, namelen); } -int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen) +SRTSTATUS getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen) { return srt::CUDT::getsockname(u, name, namelen); } -int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen) +SRTSTATUS getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen) { return srt::CUDT::getsockopt(u, level, optname, optval, optlen); } -int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen) +SRTSTATUS setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen) { return srt::CUDT::setsockopt(u, level, optname, optval, optlen); } // DEVELOPER API -int connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn) { return srt::CUDT::connect(u, name, namelen, forced_isn); } @@ -4840,37 +4850,37 @@ int epoll_create() return srt::CUDT::epoll_create(); } -int epoll_clear_usocks(int eid) +SRTSTATUS epoll_clear_usocks(int eid) { return srt::CUDT::epoll_clear_usocks(eid); } -int epoll_add_usock(int eid, SRTSOCKET u, const int* events) +SRTSTATUS epoll_add_usock(int eid, SRTSOCKET u, const int* events) { return srt::CUDT::epoll_add_usock(eid, u, events); } -int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) +SRTSTATUS epoll_add_ssock(int eid, SYSSOCKET s, const int* events) { return srt::CUDT::epoll_add_ssock(eid, s, events); } -int epoll_update_usock(int eid, SRTSOCKET u, const int* events) +SRTSTATUS epoll_update_usock(int eid, SRTSOCKET u, const int* events) { return srt::CUDT::epoll_update_usock(eid, u, events); } -int epoll_update_ssock(int eid, SYSSOCKET s, const int* events) +SRTSTATUS epoll_update_ssock(int eid, SYSSOCKET s, const int* events) { return srt::CUDT::epoll_update_ssock(eid, s, events); } -int epoll_remove_usock(int eid, SRTSOCKET u) +SRTSTATUS epoll_remove_usock(int eid, SRTSOCKET u) { return srt::CUDT::epoll_remove_usock(eid, u); } -int epoll_remove_ssock(int eid, SYSSOCKET s) +SRTSTATUS epoll_remove_ssock(int eid, SYSSOCKET s) { return srt::CUDT::epoll_remove_ssock(eid, s); } @@ -4961,7 +4971,7 @@ int epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut return srt::CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); } -int epoll_release(int eid) +SRTSTATUS epoll_release(int eid) { return srt::CUDT::epoll_release(eid); } @@ -4993,7 +5003,7 @@ const char* geterror_desc(int code, int err) return (e.getErrorMessage()); } -int bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear) +SRTSTATUS bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear) { return srt::CUDT::bstats(u, perf, clear); } @@ -5079,7 +5089,7 @@ int getrejectreason(SRTSOCKET u) return CUDT::rejectReason(u); } -int setrejectreason(SRTSOCKET u, int value) +SRTSTATUS setrejectreason(SRTSOCKET u, int value) { return CUDT::rejectReason(u, value); } diff --git a/srtcore/api.h b/srtcore/api.h index a69b4b618..d80611727 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -84,7 +84,7 @@ class CUDTSocket CUDTSocket() : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -103,7 +103,7 @@ class CUDTSocket CUDTSocket(const CUDTSocket& ancestor) : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -191,7 +191,7 @@ class CUDTSocket CUDT& core() { return m_UDT; } const CUDT& core() const { return m_UDT; } - static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { return (int64_t(id) << 30) + isn; } + static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { return (int64_t(int32_t(id)) << 30) + isn; } int64_t getPeerSpec() { return getPeerSpec(m_PeerID, m_iISN); } SRT_SOCKSTATUS getStatus(); @@ -260,11 +260,11 @@ class CUDTUnited /// initialize the UDT library. /// @return 0 if success, otherwise -1 is returned. - int startup(); + SRTRUNSTATUS startup(); /// release the UDT library. /// @return 0 if success, otherwise -1 is returned. - int cleanup(); + SRTSTATUS cleanup(); /// Create a new UDT socket. /// @param [out] pps Variable (optional) to which the new socket will be written, if succeeded @@ -294,8 +294,8 @@ class CUDTUnited void removePendingForGroup(const CUDTGroup* g); #endif - int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); - int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); + SRTSTATUS installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + SRTSTATUS installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); /// Check the status of the UDT socket. /// @param [in] u the UDT socket ID. @@ -304,20 +304,20 @@ class CUDTUnited // socket APIs - int bind(CUDTSocket* u, const sockaddr_any& name); - int bind(CUDTSocket* u, UDPSOCKET udpsock); - int listen(const SRTSOCKET u, int backlog); + SRTSTATUS bind(CUDTSocket* u, const sockaddr_any& name); + SRTSTATUS bind(CUDTSocket* u, UDPSOCKET udpsock); + SRTSTATUS listen(const SRTSOCKET u, int backlog); SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen); SRTSOCKET accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); - int connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); - int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - int connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); + SRTSOCKET connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); + SRTSOCKET connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + void connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); #if ENABLE_BONDING - int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); - int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); + SRTSOCKET groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); + SRTSOCKET singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); #endif - int close(const SRTSOCKET u); - int close(CUDTSocket* s); + SRTSTATUS close(const SRTSOCKET u); + SRTSTATUS close(CUDTSocket* s); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockdevname(const SRTSOCKET u, char* name, size_t* namelen); @@ -328,22 +328,22 @@ class CUDTUnited std::vector* exceptfds, int64_t msTimeOut); int epoll_create(); - int epoll_clear_usocks(int eid); - int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - int epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); - int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_remove_usock(const int eid, const SRTSOCKET u); + void epoll_clear_usocks(int eid); + void epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + void epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); + void epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + void epoll_remove_usock(const int eid, const SRTSOCKET u); template - int epoll_remove_entity(const int eid, EntityType* ent); - int epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); + void epoll_remove_entity(const int eid, EntityType* ent); + void epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); #if ENABLE_BONDING - int epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); + void epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); #endif - int epoll_remove_ssock(const int eid, const SYSSOCKET s); - int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); + void epoll_remove_ssock(const int eid, const SYSSOCKET s); + void epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); int32_t epoll_set(const int eid, int32_t flags); - int epoll_release(const int eid); + void epoll_release(const int eid); #if ENABLE_BONDING SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_GlobControlLock) @@ -420,8 +420,8 @@ class CUDTUnited 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 + int32_t m_SocketIDGenerator; // seed to generate a new unique socket ID + int32_t m_SocketIDGenerator_init; // Keeps track of the very first one SRT_ATTR_GUARDED_BY(m_GlobControlLock) std::map > diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index e74d48c07..59d534003 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -272,6 +272,10 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage(): %(" << seqnolo << " - " << seqnohi << ")" << " #" << msgno << " actionOnExisting=" << actionOnExisting << " m_iStartSeqNo=%" << m_iStartSeqNo); + if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. + { + HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with msgflag field set to a negative value!"); + } // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7bede5a4a..5b3eb2262 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -92,8 +92,8 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; -const int UDT::ERROR = srt::CUDT::ERROR; +const SRTSOCKET UDT::INVALID_SOCK = SRT_INVALID_SOCK; +const SRTSTATUS UDT::ERROR = SRT_ERROR; //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -283,7 +283,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_PeerID = SRT_SOCKID_CONNREQ; m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -1345,7 +1345,7 @@ size_t srt::CUDT::fillHsExtGroup(uint32_t* pcmdspec) | SrtHSRequest::HS_GROUP_FLAGS::wrap(flags) | SrtHSRequest::HS_GROUP_WEIGHT::wrap(m_parent->m_GroupMemberData->weight); - const uint32_t storedata [GRPD_E_SIZE] = { uint32_t(id), dataword }; + const uint32_t storedata [GRPD_E_SIZE] = { uint32_t(int(id)), dataword }; memcpy((space), storedata, sizeof storedata); const size_t ra_size = Size(storedata); @@ -3133,7 +3133,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // for consistency and possibly changes in future. // We are granted these two fields do exist - SRTSOCKET grpid = groupdata[GRPD_GROUPID]; + SRTSOCKET grpid = SRTSOCKET(groupdata[GRPD_GROUPID]); uint32_t gd = groupdata[GRPD_GROUPDATA]; SRT_GROUP_TYPE gtp = SRT_GROUP_TYPE(SrtHSRequest::HS_GROUP_TYPE::unwrap(gd)); @@ -3156,7 +3156,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A return false; } - if ((grpid & SRTGROUP_MASK) == 0) + if (!isgroup(grpid)) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS/GROUP: socket ID passed as a group ID is not a group ID"); @@ -3226,7 +3226,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A } SRTSOCKET peer = pg->peerid(); - if (peer == -1) + if (peer == SRT_INVALID_SOCK) { // This is the first connection within this group, so this group // has just been informed about the peer membership. Accept it. @@ -3268,10 +3268,10 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // ID to the peer. SRTSOCKET lgid = makeMePeerOf(grpid, gtp, link_flags); - if (!lgid) + if (lgid == SRT_SOCKID_CONNREQ) return true; // already done - if (lgid == -1) + if (lgid == SRT_INVALID_SOCK) { // NOTE: This error currently isn't reported by makeMePeerOf, // so this is left to handle a possible error introduced in future. @@ -3328,7 +3328,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp << " agent group=$" << gp->id() << " type" << gp->type()); - return -1; + return SRT_INVALID_SOCK; } HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id()); @@ -3345,14 +3345,14 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 catch (...) { // Expected exceptions are only those referring to system resources - return -1; + return SRT_INVALID_SOCK; } if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. uglobal().deleteGroup_LOCKED(gp); - return -1; + return SRT_INVALID_SOCK; } gp->set_peerid(peergroup); @@ -3397,7 +3397,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); s->m_GroupOf = gp; s->m_GroupMemberData = f; - return 0; + return SRT_SOCKID_CONNREQ; } s->m_GroupMemberData = gp->add(groups::prepareSocketData(s)); @@ -3619,7 +3619,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.set_id(0); + reqpkt.set_id(SRT_SOCKID_CONNREQ); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3954,7 +3954,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - reqpkt.set_id(!m_config.bRendezvous ? 0 : m_ConnRes.m_iID); + reqpkt.set_id(!m_config.bRendezvous ? SRT_SOCKID_CONNREQ : m_ConnRes.m_iID); bool status = true; @@ -7137,7 +7137,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return 0; // Forced to return error instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0), int(SRT_ERROR); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } else @@ -7276,7 +7276,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { // Forced to return 0 instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0).as(); if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -7285,7 +7285,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { // Forced to return -1 instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_NOCONN, 0); + return APIError(MJ_CONNECTION, MN_NOCONN, 0).as(); throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); } } while ((res == 0) && !timeout); @@ -7317,7 +7317,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { // Forced to return -1 instead of throwing exception. if (!by_exception) - return APIError(MJ_AGAIN, MN_XMTIMEOUT, 0); + return APIError(MJ_AGAIN, MN_XMTIMEOUT, 0).as(); throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); } @@ -7998,7 +7998,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); ctrlpkt.set_id(m_PeerID); - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -8085,7 +8085,7 @@ 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. + if (m_PeerID == SRT_SOCKID_CONNREQ) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); ctrlpkt.set_id(m_PeerID); @@ -9156,6 +9156,27 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { + typedef int32_t expected_t[2]; + if (ctrlpkt.getLength() < sizeof (expected_t)) + { + // We ALLOW packets that are bigger than this to allow + // future extensions, this just interprets the part that + // is expected, and reject only those that don't carry + // even the required data. + LOGC(brlog.Error, log << CONID() << "EPE: Wrong size of the DROPREQ message: " << ctrlpkt.getLength() + << " - expected >=" << sizeof(expected_t)); + return; + } + + int32_t msgno = ctrlpkt.getMsgSeq(m_bPeerRexmitFlag); + + // Check for rogue message + if (msgno == SRT_MSGNO_NONE) + { + LOGC(brlog.Warn, log << CONID() << "ROGUE DROPREQ detected with #NONE - fallback: fixing to #CONTROL"); + msgno = SRT_MSGNO_CONTROL; + } + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; { @@ -9166,9 +9187,8 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // 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); - const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag), CRcvBuffer::KEEP_EXISTING); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], msgno, CRcvBuffer::KEEP_EXISTING); if (iDropCnt > 0) { @@ -9178,7 +9198,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) if (frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) { LOGC(brlog.Warn, log << CONID() << "RCV-DROPPED " << iDropCnt << " packet(s), seqno range %" - << dropdata[0] << "-%" << dropdata[1] << ", msgno " << ctrlpkt.getMsgSeq(using_rexmit_flag) + << dropdata[0] << "-%" << dropdata[1] << ", #" << ctrlpkt.getMsgSeq(m_bPeerRexmitFlag) << " (SND DROP REQUEST). " << why); } #if SRT_ENABLE_FREQUENT_LOG_TRACE @@ -9273,7 +9293,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.id()); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=@" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -11978,7 +11998,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u) return s->core().m_RejectReason; } -int srt::CUDT::rejectReason(SRTSOCKET u, int value) +SRTSTATUS srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = uglobal().locateSocket(u); if (!s) @@ -11988,14 +12008,14 @@ int srt::CUDT::rejectReason(SRTSOCKET u, int value) return APIError(MJ_NOTSUP, MN_INVAL); s->core().m_RejectReason = value; - return 0; + return SRT_STATUS_OK; } int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { CUDTSocket* s = uglobal().locateSocket(u); if (!s) - return APIError(MJ_NOTSUP, MN_SIDINVAL); + return APIError(MJ_NOTSUP, MN_SIDINVAL).as(); const time_point& start_time = s->core().m_stats.tsStartTime; return count_microseconds(start_time.time_since_epoch()); diff --git a/srtcore/core.h b/srtcore/core.h index 501f6f352..cc1f64789 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -195,31 +195,31 @@ class CUDT ~CUDT(); public: //API - static int startup(); - static int cleanup(); + static SRTRUNSTATUS startup(); + static SRTSTATUS cleanup(); static SRTSOCKET socket(); #if ENABLE_BONDING static SRTSOCKET createGroup(SRT_GROUP_TYPE); static SRTSOCKET getGroupOfSocket(SRTSOCKET socket); - static int getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize); - static bool isgroup(SRTSOCKET sock) { return (sock & SRTGROUP_MASK) != 0; } + static SRTSTATUS getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize); + static bool isgroup(SRTSOCKET sock) { return (int32_t(sock) & SRTGROUP_MASK) != 0; } #endif - static int bind(SRTSOCKET u, const sockaddr* name, int namelen); - static int bind(SRTSOCKET u, UDPSOCKET udpsock); - static int listen(SRTSOCKET u, int backlog); + static SRTSTATUS bind(SRTSOCKET u, const sockaddr* name, int namelen); + static SRTSTATUS bind(SRTSOCKET u, UDPSOCKET udpsock); + static SRTSTATUS listen(SRTSOCKET u, int backlog); static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen); static SRTSOCKET accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut); - static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - static int connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); #if ENABLE_BONDING - static int connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); + static SRTSOCKET connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); #endif - static int close(SRTSOCKET u); - static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockdevname(SRTSOCKET u, char* name, size_t* namelen); - static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); - static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); + static SRTSTATUS close(SRTSOCKET u); + static SRTSTATUS getpeername(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockname(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockdevname(SRTSOCKET u, char* name, size_t* namelen); + static SRTSTATUS getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); + static SRTSTATUS setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); static int send(SRTSOCKET u, const char* buf, int len, int flags); static int recv(SRTSOCKET u, char* buf, int len, int flags); static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = SRT_MSGTTL_INF, bool inorder = false, int64_t srctime = 0); @@ -231,29 +231,29 @@ class CUDT static int select(int nfds, UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); static int epoll_create(); - static int epoll_clear_usocks(int eid); - static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - static int epoll_remove_usock(const int eid, const SRTSOCKET u); - static int epoll_remove_ssock(const int eid, const SYSSOCKET s); - static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_clear_usocks(int eid); + static SRTSTATUS epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_remove_usock(const int eid, const SRTSOCKET u); + static SRTSTATUS epoll_remove_ssock(const int eid, const SYSSOCKET s); + static SRTSTATUS epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); static int32_t epoll_set(const int eid, int32_t flags); - static int epoll_release(const int eid); + static SRTSTATUS epoll_release(const int eid); static CUDTException& getlasterror(); - static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); + static SRTSTATUS bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); #if ENABLE_BONDING - static int groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); + static SRTSTATUS groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); #endif static SRT_SOCKSTATUS getsockstate(SRTSOCKET u); static bool setstreamid(SRTSOCKET u, const std::string& sid); static std::string getstreamid(SRTSOCKET u); - static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); + static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); // returns buffer span in [ms] static int rejectReason(SRTSOCKET s); - static int rejectReason(SRTSOCKET s, int value); + static SRTSTATUS rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); static int getMaxPayloadSize(SRTSOCKET u); @@ -263,15 +263,20 @@ class CUDT { APIError(const CUDTException&); APIError(CodeMajor, CodeMinor, int = 0); + APIError(int error_code); - operator int() const + // This represents both SRT_ERROR and SRT_INVALID_SOCK. + operator SRTSTATUS() const { return SRT_ERROR; } - }; - static const SRTSOCKET INVALID_SOCK = -1; // Invalid socket descriptor - static const int ERROR = -1; // Socket api error returned value + template + Retval as() const + { + return Retval((int)SRT_ERROR); + } + }; static const int HS_VERSION_UDT4 = 4; static const int HS_VERSION_SRT1 = 5; @@ -299,7 +304,7 @@ class CUDT { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_SocketID << ": "; + os << "@" << int(m_SocketID) << ": "; return os.str(); #else return ""; @@ -1010,8 +1015,8 @@ class CUDT CallbackHolder m_cbConnectHook; // FORWARDER public: - static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); - static int installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); + static SRTSTATUS installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + static SRTSTATUS installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); private: void installAcceptHook(srt_listen_callback_fn* hook, void* opaq) { diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index e7fe0be89..d61e7dc9a 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -723,7 +723,7 @@ void srt::CCryptoControl::close() std::string srt::CCryptoControl::CONID() const { - if (m_SocketID == 0) + if (int32_t(m_SocketID) <= 0) return ""; std::ostringstream os; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 3a607c3a1..6f03f5adc 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -159,7 +159,7 @@ ENOMEM: There was insufficient memory to create the kernel object. return m_iIDSeed; } -int srt::CEPoll::clear_usocks(int eid) +void srt::CEPoll::clear_usocks(int eid) { // This should remove all SRT sockets from given eid. ScopedLock pg (m_EPollLock); @@ -171,8 +171,6 @@ int srt::CEPoll::clear_usocks(int eid) CEPollDesc& d = p->second; d.clearAll(); - - return 0; } @@ -213,7 +211,7 @@ void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction) d.removeSubscription(cleared[j]); } -int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -281,11 +279,9 @@ int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) #endif p->second.m_sLocals.insert(s); - - return 0; } -int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +void srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) { ScopedLock pg(m_EPollLock); @@ -311,12 +307,10 @@ int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) #endif p->second.m_sLocals.erase(s); - - return 0; } // Need this to atomically modify polled events (ex: remove write/keep read) -int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) +void srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) { ScopedLock pg(m_EPollLock); IF_HEAVY_LOGGING(ostringstream evd); @@ -385,10 +379,9 @@ int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* even HLOGC(ealog.Debug, log << "srt_epoll_update_usock: REMOVED E" << eid << " socket @" << u); d.removeSubscription(u); } - return 0; } -int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -456,10 +449,9 @@ int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* even // Assuming add is used if not inserted // p->second.m_sLocals.insert(s); - return 0; } -int srt::CEPoll::setflags(const int eid, int32_t flags) +int32_t srt::CEPoll::setflags(const int eid, int32_t flags) { ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); @@ -854,7 +846,7 @@ bool srt::CEPoll::empty(const CEPollDesc& d) const return d.watch_empty(); } -int srt::CEPoll::release(const int eid) +void srt::CEPoll::release(const int eid) { ScopedLock pg(m_EPollLock); @@ -870,8 +862,6 @@ int srt::CEPoll::release(const int eid) #endif m_mPolls.erase(i); - - return 0; } @@ -881,13 +871,13 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const if ((events & ~SRT_EPOLL_EVENTTYPES) != 0) { LOGC(eilog.Fatal, log << "epoll/update: IPE: 'events' parameter shall not contain special flags!"); - return -1; // still, ignored. + return int(SRT_ERROR); // still, ignored. } - if (uid == SRT_INVALID_SOCK || uid == 0) // XXX change to a symbolic value + if (uid == SRT_INVALID_SOCK || uid == SRT_SOCKID_CONNREQ) { LOGC(eilog.Fatal, log << "epoll/update: IPE: invalid 'uid' submitted for update!"); - return -1; + return int(SRT_ERROR); } int nupdated = 0; diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 3ac190186..e82fef38a 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -377,38 +377,29 @@ friend class srt::CRendezvousQueue; /// delete all user sockets (SRT sockets) from an EPoll /// @param [in] eid EPoll ID. - /// @return 0 - int clear_usocks(int eid); + void clear_usocks(int eid); /// add a system socket to an EPoll. /// @param [in] eid EPoll ID. /// @param [in] s system Socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// remove a system socket event from an EPoll; socket will be removed if no events to watch. /// @param [in] eid EPoll ID. /// @param [in] s system socket ID. - /// @return 0 if success, otherwise an error number. - - int remove_ssock(const int eid, const SYSSOCKET& s); + void remove_ssock(const int eid, const SYSSOCKET& s); /// update a UDT socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_usock(const int eid, const SRTSOCKET& u, const int* events); + void update_usock(const int eid, const SRTSOCKET& u, const int* events); /// update a system socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// wait for EPoll events or timeout. /// @param [in] eid EPoll ID. @@ -475,7 +466,7 @@ friend class srt::CRendezvousQueue; /// @param [in] eid EPoll ID. /// @return 0 if success, otherwise an error number. - int release(const int eid); + void release(const int eid); public: // for CUDT to acknowledge IO status @@ -491,7 +482,7 @@ friend class srt::CRendezvousQueue; void wipe_usock(const SRTSOCKET uid, std::set& eids); - int setflags(const int eid, int32_t flags); + int32_t setflags(const int eid, int32_t flags); private: int m_iIDSeed; // seed to generate a new ID diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index fd762b88b..6ec11e13e 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -1447,7 +1447,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) ; p.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; - p.hdr[SRT_PH_ID] = rcv.id; + p.hdr[SRT_PH_ID] = int32_t(rcv.id); // Header ready, now we rebuild the contents // First, rebuild the length. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 9fed5e539..ed4708cef 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -249,8 +249,8 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_Global(CUDT::uglobal()) - , m_GroupID(-1) - , m_PeerGroupID(-1) + , m_GroupID(SRT_INVALID_SOCK) + , m_PeerGroupID(SRT_INVALID_SOCK) , m_type(gtype) , m_iBusy() , m_iSndOldestMsgNo(SRT_MSGNO_NONE) @@ -1075,7 +1075,7 @@ void CUDTGroup::close() // removing themselves from the group when closing because they // are unaware of being group members. m_Group.clear(); - m_PeerGroupID = -1; + m_PeerGroupID = SRT_INVALID_SOCK; set epollid; { @@ -1915,7 +1915,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) return rstat; } -int CUDTGroup::getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize) +SRTSTATUS CUDTGroup::getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize) { if (!psize) return CUDT::APIError(MJ_NOTSUP, MN_INVAL); @@ -1926,7 +1926,7 @@ int CUDTGroup::getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize) } // [[using locked(this->m_GroupLock)]] -int CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize) +SRTSTATUS CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize) { SRT_ASSERT(psize != NULL); const size_t size = *psize; @@ -1935,7 +1935,9 @@ int CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize) if (!pdata) { - return 0; + // The request was only to get the number of group members, + // already filled. + return SRT_STATUS_OK; } if (m_Group.size() > size) @@ -1950,7 +1952,7 @@ int CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize) copyGroupData(*d, (pdata[i])); } - return (int)m_Group.size(); + return SRT_STATUS_OK; } // [[using locked(this->m_GroupLock)]] @@ -1971,20 +1973,20 @@ void CUDTGroup::copyGroupData(const CUDTGroup::SocketData& source, SRT_SOCKGROUP if (source.sndstate == SRT_GST_RUNNING || source.rcvstate == SRT_GST_RUNNING) { - w_target.result = 0; + w_target.result = SRT_STATUS_OK; w_target.memberstate = SRT_GST_RUNNING; } // Stats can differ per direction only // when at least in one direction it's ACTIVE. else if (source.sndstate == SRT_GST_BROKEN || source.rcvstate == SRT_GST_BROKEN) { - w_target.result = -1; + w_target.result = SRT_ERROR; w_target.memberstate = SRT_GST_BROKEN; } else { // IDLE or PENDING - w_target.result = 0; + w_target.result = SRT_STATUS_OK; w_target.memberstate = source.sndstate; } @@ -2040,7 +2042,7 @@ void CUDTGroup::fillGroupData(SRT_MSGCTRL& w_out, // MSGCTRL to be written return; } - int st = getGroupData_LOCKED((grpdata), (&grpdata_size)); + SRTSTATUS st = getGroupData_LOCKED((grpdata), (&grpdata_size)); // Always write back the size, no matter if the data were filled. w_out.grpdata_size = grpdata_size; @@ -2521,7 +2523,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // This socket will not be socketToRead in the next turn because receiveMessage() return 0 here. continue; } - if (res == SRT_ERROR) + if (res == int(SRT_ERROR)) { LOGC(grlog.Warn, log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": " << srt_getlasterror_str() @@ -3967,7 +3969,7 @@ int CUDTGroup::sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& SRT_ASSERT(w_nsuccessful == 0); SRT_ASSERT(w_maxActiveWeight == 0); - int group_send_result = SRT_ERROR; + int group_send_result = int(SRT_ERROR); // TODO: implement iterator over active links typedef vector::const_iterator const_iter_t; @@ -3981,7 +3983,7 @@ int CUDTGroup::sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& // 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; + int sndresult = int(SRT_ERROR); try { // This must be wrapped in try-catch because on error it throws an exception. @@ -3994,7 +3996,7 @@ int CUDTGroup::sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& { w_cx = e; erc = e.getErrorCode(); - sndresult = SRT_ERROR; + sndresult = int(SRT_ERROR); } const bool send_succeeded = sendBackup_CheckSendStatus( diff --git a/srtcore/group.h b/srtcore/group.h index 6fd5c5913..6794d9d49 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -378,8 +378,8 @@ class CUDTGroup void readyPackets(srt::CUDT* core, int32_t ack); 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); + SRTSTATUS getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize); + SRTSTATUS getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize); /// Predicted to be called from the reading function to fill /// the group data array as requested. @@ -769,7 +769,7 @@ class CUDTGroup { #if ENABLE_LOGGING std::ostringstream os; - os << "$" << m_GroupID << ":"; + os << "$" << int(m_GroupID) << ":"; return os.str(); #else return ""; diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index c97b4e2a3..079417d69 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -88,7 +88,7 @@ int srt::CHandShake::store_to(char* buf, size_t& w_size) *p++ = m_iMSS; *p++ = m_iFlightFlagSize; *p++ = int32_t(m_iReqType); - *p++ = m_iID; + *p++ = int32_t(m_iID); *p++ = m_iCookie; for (int i = 0; i < 4; ++ i) *p++ = m_piPeerIP[i]; @@ -111,7 +111,7 @@ int srt::CHandShake::load_from(const char* buf, size_t size) m_iMSS = *p++; m_iFlightFlagSize = *p++; m_iReqType = UDTRequestType(*p++); - m_iID = *p++; + m_iID = SRTSOCKET(*p++); m_iCookie = *p++; for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = *p++; diff --git a/srtcore/handshake.h b/srtcore/handshake.h index c6a5731e4..9b4dc1c91 100644 --- a/srtcore/handshake.h +++ b/srtcore/handshake.h @@ -320,7 +320,7 @@ class CHandShake int32_t m_iMSS; // maximum segment size int32_t m_iFlightFlagSize; // flow control window size UDTRequestType m_iReqType; // handshake stage - int32_t m_iID; // SRT socket ID of HS sender + SRTSOCKET m_iID; // SRT socket ID of HS sender int32_t m_iCookie; // cookie uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index edf7a12bf..224bc2794 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -742,10 +742,10 @@ void srt::CHash::init(int size) m_iHashSize = size; } -srt::CUDT* srt::CHash::lookup(int32_t id) +srt::CUDT* srt::CHash::lookup(SRTSOCKET id) { // simple hash function (% hash table size); suitable for socket descriptors - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); while (NULL != b) { @@ -757,18 +757,18 @@ srt::CUDT* srt::CHash::lookup(int32_t id) return NULL; } -srt::CUDT* srt::CHash::lookupPeer(int32_t peerid) +srt::CUDT* srt::CHash::lookupPeer(SRTSOCKET peerid) { // Decode back the socket ID if it has that peer - int32_t id = map_get(m_RevPeerMap, peerid, -1); - if (id == -1) + SRTSOCKET id = map_get(m_RevPeerMap, peerid, SRT_INVALID_SOCK); + if (id == SRT_INVALID_SOCK) return NULL; // no such peer id return lookup(id); } -void srt::CHash::insert(int32_t id, CUDT* u) +void srt::CHash::insert(SRTSOCKET id, CUDT* u) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* n = new CBucket; n->m_iID = id; @@ -776,13 +776,13 @@ void srt::CHash::insert(int32_t id, CUDT* u) n->m_pUDT = u; n->m_pNext = b; - m_pBucket[id % m_iHashSize] = n; + bucketAt(id) = n; m_RevPeerMap[u->peerID()] = id; } -void srt::CHash::remove(int32_t id) +void srt::CHash::remove(SRTSOCKET id) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* p = NULL; while (NULL != b) @@ -790,7 +790,7 @@ void srt::CHash::remove(int32_t id) if (id == b->m_iID) { if (NULL == p) - m_pBucket[id % m_iHashSize] = b->m_pNext; + bucketAt(id) = b->m_pNext; else p->m_pNext = b->m_pNext; @@ -854,12 +854,12 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& { ScopedLock vg(m_RIDListLock); - IF_HEAVY_LOGGING(const char* const id_type = w_id ? "THIS ID" : "A NEW CONNECTION"); + IF_HEAVY_LOGGING(const char* const id_type = w_id == SRT_SOCKID_CONNREQ ? "A NEW CONNECTION" : "THIS ID" ); // 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))) + if (i->m_PeerAddr == addr && ((w_id == SRT_SOCKID_CONNREQ) || (w_id == i->m_iID))) { // This procedure doesn't exactly respond to the original UDT idea. // As the "rendezvous queue" is used for both handling rendezvous and @@ -878,7 +878,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& // This means: if an incoming ID is 0, then this search should succeed ONLY // IF THE FOUND SOCKET WAS RENDEZVOUS. - if (!w_id && !i->m_pUDT->m_config.bRendezvous) + if (w_id == SRT_SOCKID_CONNREQ && !i->m_pUDT->m_config.bRendezvous) { HLOGC(cnlog.Debug, log << "RID: found id @" << i->m_iID << " while looking for " @@ -897,7 +897,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& #if ENABLE_HEAVY_LOGGING std::ostringstream spec; - if (w_id == 0) + if (w_id == SRT_SOCKID_CONNREQ) spec << "A NEW CONNECTION REQUEST"; else spec << " AGENT @" << w_id; @@ -917,7 +917,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->id() : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : SRT_SOCKID_CONNREQ; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -958,7 +958,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst } - if (cst != CONN_RENDEZVOUS && dest_id != 0) + if (cst != CONN_RENDEZVOUS && dest_id != SRT_SOCKID_CONNREQ) { if (i->id != dest_id) { @@ -1057,7 +1057,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, - int iDstSockID, + SRTSOCKET iDstSockID, vector& toRemove, vector& toProcess) { @@ -1195,7 +1195,7 @@ srt::CRcvQueue::~CRcvQueue() delete m_pRendezvousQueue; // remove all queued messages - for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + for (qmap_t::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) { while (!i->second.empty()) { @@ -1244,7 +1244,7 @@ void* srt::CRcvQueue::worker(void* param) { CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->getIPversion()); - int32_t id = 0; + SRTSOCKET id = SRT_SOCKID_CONNREQ; std::string thname; ThreadName::get(thname); @@ -1260,7 +1260,7 @@ void* srt::CRcvQueue::worker(void* param) INCREMENT_THREAD_ITERATIONS(); if (rst == RST_OK) { - if (id < 0) + if (int(id) < 0) // Any negative (illegal range) and SRT_INVALID_SOCKET { // User error on peer. May log something, but generally can only ignore it. // XXX Think maybe about sending some "connection rejection response". @@ -1277,7 +1277,7 @@ void* srt::CRcvQueue::worker(void* param) // Note to rendezvous connection. This can accept: // - ID == 0 - take the first waiting rendezvous socket // - ID > 0 - find the rendezvous socket that has this ID. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets cst = self->worker_ProcessConnectionRequest(unit, sa); @@ -1373,7 +1373,7 @@ void* srt::CRcvQueue::worker(void* param) return NULL; } -srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) +srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(SRTSOCKET& w_id, CUnit*& w_unit, sockaddr_any& w_addr) { #if !USE_BUSY_WAITING // This might be not really necessary, and probably @@ -1483,7 +1483,9 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, } // If there's no listener waiting for the packet, just store it into the queue. - return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. + // Passing SRT_SOCKID_CONNREQ explicitly because it's a handler for a HS packet + // that came for this very ID. + return worker_TryAsyncRend_OrStore(SRT_SOCKID_CONNREQ, unit, addr); } bool srt::CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) @@ -1505,7 +1507,7 @@ bool srt::CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& a hs.m_extension = true; // Ok, at last we have a peer ID info - int32_t peerid = hs.m_iID; + SRTSOCKET peerid = hs.m_iID; // Now search for a socket that has this peer ID CUDT* u = m_pHash->lookupPeer(peerid); @@ -1536,7 +1538,7 @@ bool srt::CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& a return u->createSendHSResponse(kmdata, kmdatasize, pkt.udpDestAddr(), (hs)); } -srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); if (!u) @@ -1594,7 +1596,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CU // 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. -srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(SRTSOCKET 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) @@ -1615,7 +1617,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni // not belonging to the connection and not registered as rendezvous) as "possible // attack" and ignore it. This also should better protect the rendezvous socket // against a rogue connector. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { HLOGC(cnlog.Debug, log << CONID() << "AsyncOrRND: no sockets expect connection from " << addr.str() @@ -1741,11 +1743,11 @@ void srt::CRcvQueue::stopWorker() m_WorkerThread.join(); } -int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) +int srt::CRcvQueue::recvfrom(SRTSOCKET id, CPacket& w_packet) { CUniqueSync buffercond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1824,7 +1826,7 @@ void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) ScopedLock bufferlock(m_BufferLock); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { HLOGC(cnlog.Debug, @@ -1869,11 +1871,11 @@ void srt::CRcvQueue::kick() CSync::lock_notify_all(m_BufferCond, m_BufferLock); } -void srt::CRcvQueue::storePktClone(int32_t id, const CPacket& pkt) +void srt::CRcvQueue::storePktClone(SRTSOCKET id, const CPacket& pkt) { CUniqueSync passcond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { diff --git a/srtcore/queue.h b/srtcore/queue.h index 166fe43a3..0e2680e57 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -279,30 +279,30 @@ class CHash /// @param [in] id socket ID /// @return Pointer to a UDT instance, or NULL if not found. - CUDT* lookup(int32_t id); + CUDT* lookup(SRTSOCKET id); /// Look for a UDT instance from the hash table by source ID /// @param [in] peerid socket ID of the peer reported as source ID /// @return Pointer to a UDT instance where m_PeerID == peerid, or NULL if not found - CUDT* lookupPeer(int32_t peerid); + CUDT* lookupPeer(SRTSOCKET peerid); /// 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(SRTSOCKET id, CUDT* u); /// Remove an entry from the hash table. /// @param [in] id socket ID - void remove(int32_t id); + void remove(SRTSOCKET id); private: struct CBucket { - int32_t m_iID; // Socket ID - int32_t m_iPeerID; // Peer ID + SRTSOCKET m_iID; // Socket ID + SRTSOCKET m_iPeerID; // Peer ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -310,7 +310,12 @@ class CHash int m_iHashSize; // size of hash table - std::map m_RevPeerMap; + std::map m_RevPeerMap; + + CBucket*& bucketAt(SRTSOCKET id) + { + return m_pBucket[int32_t(id) % m_iHashSize]; + } private: CHash(const CHash&); @@ -383,7 +388,7 @@ class CRendezvousQueue /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, - int iDstSockID, + SRTSOCKET iDstSockID, std::vector& toRemove, std::vector& toProcess); @@ -514,7 +519,7 @@ class CRcvQueue /// @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(SRTSOCKET id, CPacket& to_packet); void stopWorker(); @@ -526,10 +531,10 @@ class CRcvQueue static void* worker(void* param); sync::CThread m_WorkerThread; // Subroutines of worker - EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); + EReadStatus worker_RetrieveUnit(SRTSOCKET& 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); + EConnectStatus worker_TryAsyncRend_OrStore(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); bool worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr); private: @@ -561,7 +566,7 @@ class CRcvQueue bool ifNewEntry(); CUDT* getNewEntry(); - void storePktClone(int32_t id, const CPacket& pkt); + void storePktClone(SRTSOCKET id, const CPacket& pkt); void kick(); @@ -572,9 +577,10 @@ class CRcvQueue std::vector m_vNewEntry; // newly added entries, to be inserted sync::Mutex m_IDLock; - std::map > m_mBuffer; // temporary buffer for rendezvous connection request - sync::Mutex m_BufferLock; - sync::Condition m_BufferCond; + typedef std::map > qmap_t; + qmap_t m_mBuffer; // temporary buffer for rendezvous connection request + sync::Mutex m_BufferLock; + sync::Condition m_BufferCond; private: CRcvQueue(const CRcvQueue&); diff --git a/srtcore/srt.h b/srtcore/srt.h index fa77054f8..4151caad7 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -126,12 +126,28 @@ written by #define SRT_ATR_NODISCARD #endif +// The SRT_TEST_FORCED_CONSTANT macro enables the strict +// version of SRTSOCKET and SRTSTATUS types. This allows you +// to detect constant type violations by compiling the code +// with the C++20 compliant compiler and the following cmake +// variables set: +// * USE_CXX_STD=c++20 +// * ENFORCE_SRT_TEST_FORCED_CONSTANT=1 +#ifndef SRT_TEST_FORCED_CONSTANT +// This is normal and should be normally used. +typedef int32_t SRTSOCKET; +typedef int SRTSTATUS; +typedef int SRTRUNSTATUS; +#else +// Used for development only. +#include "../common/devel_util.h" +#endif + + #ifdef __cplusplus extern "C" { #endif -typedef int32_t SRTSOCKET; - // The most significant bit 31 (sign bit actually) is left unused, // so that all people who check the value for < 0 instead of -1 // still get what they want. The bit 30 is reserved for marking @@ -752,16 +768,33 @@ inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2) return SRT_EPOLL_OPT( (int)a1 | (int)a2 ); } +static const SRTSOCKET SRT_INVALID_SOCK (-1); +static const SRTSOCKET SRT_SOCKID_CONNREQ (0); +static const SRTSTATUS SRT_ERROR (-1); +static const SRTSTATUS SRT_STATUS_OK (0); +static const SRTRUNSTATUS SRT_RUN_ERROR (-1); +static const SRTRUNSTATUS SRT_RUN_OK (0); +static const SRTRUNSTATUS SRT_RUN_ALREADY (1); + + +#else // C version + +static const SRTSOCKET SRT_INVALID_SOCK = -1; +static const SRTSOCKET SRT_SOCKID_CONNREQ = 0; +static const SRTSTATUS SRT_ERROR = -1; +static const SRTSTATUS SRT_STATUS_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ERROR = -1; +static const SRTRUNSTATUS SRT_RUN_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ALREADY = 1; + #endif typedef struct CBytePerfMon SRT_TRACEBSTATS; -static const SRTSOCKET SRT_INVALID_SOCK = -1; -static const int SRT_ERROR = -1; // library initialization -SRT_API int srt_startup(void); -SRT_API int srt_cleanup(void); +SRT_API SRTRUNSTATUS srt_startup(void); +SRT_API SRTSTATUS srt_cleanup(void); // // Socket operations @@ -772,34 +805,34 @@ SRT_API int srt_cleanup(void); SRT_ATR_DEPRECATED_PX SRT_API SRTSOCKET srt_socket(int, int, int) SRT_ATR_DEPRECATED; SRT_API SRTSOCKET srt_create_socket(void); -SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); +SRT_API SRTSTATUS srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSTATUS srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); // Old name of srt_bind_acquire(), please don't use // Planned deprecation removal: rel1.6.0 -SRT_ATR_DEPRECATED_PX static inline int srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; -static inline int srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } -SRT_API int srt_listen (SRTSOCKET u, int backlog); +SRT_ATR_DEPRECATED_PX static inline SRTSTATUS srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; +static inline SRTSTATUS srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } +SRT_API SRTSTATUS srt_listen (SRTSOCKET u, int backlog); SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen); SRT_API SRTSOCKET srt_accept_bond (const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid); -SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); typedef void srt_connect_callback_fn (void* opaq, SRTSOCKET ns, int errorcode, const struct sockaddr* peeraddr, int token); -SRT_API int srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); -SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); -SRT_API int srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, +SRT_API SRTSTATUS srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSOCKET srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRT_API SRTSOCKET srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); -SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRT_API SRTSTATUS srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); -SRT_API int srt_close (SRTSOCKET u); -SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockdevname(SRTSOCKET u, char* name, size_t* namelen); -SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); -SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); -SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); -SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API SRTSTATUS srt_close (SRTSOCKET u); +SRT_API SRTSTATUS srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); +SRT_API SRTSTATUS srt_getsockdevname(SRTSOCKET u, char* name, size_t* namelen); +SRT_API SRTSTATUS srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); +SRT_API SRTSTATUS srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); +SRT_API SRTSTATUS srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); SRT_API int srt_getmaxpayloadsize(SRTSOCKET sock); @@ -877,27 +910,27 @@ SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int // last error detection SRT_API const char* srt_getlasterror_str(void); -SRT_API int srt_getlasterror(int* errno_loc); +SRT_API int srt_getlasterror(int* errno_loc); SRT_API const char* srt_strerror(int code, int errnoval); SRT_API void srt_clearlasterror(void); // Performance tracking // Performance monitor with Byte counters for better bitrate estimation. -SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRT_API SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRT_API SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); // Socket Status (for problem tracking) SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u); -SRT_API int srt_epoll_create(void); -SRT_API int srt_epoll_clear_usocks(int eid); -SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u); -SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s); -SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API int srt_epoll_create(void); +SRT_API SRTSTATUS srt_epoll_clear_usocks(int eid); +SRT_API SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRT_API SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRT_API SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); SRT_API 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); @@ -907,13 +940,13 @@ 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(): fd(-1), events(0) {} // NOTE: allows singular values, no init. + SRT_EPOLL_EVENT_STR(): fd(SRT_INVALID_SOCK), 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); SRT_API int32_t srt_epoll_set(int eid, int32_t flags); -SRT_API int srt_epoll_release(int eid); +SRT_API SRTSTATUS srt_epoll_release(int eid); // Logging control @@ -931,7 +964,7 @@ SRT_API void srt_setlogflags(int flags); 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 SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); SRT_API const char* srt_rejectreason_str(int id); SRT_API const char* srt_rejectreasonx_str(int id); @@ -983,7 +1016,7 @@ struct SRT_SocketGroupData_ SRT_SOCKSTATUS sockstate; uint16_t weight; SRT_MEMBERSTATUS memberstate; - int result; + SRTSTATUS result; int token; }; @@ -1002,14 +1035,14 @@ typedef struct SRT_GroupMemberConfig_ SRT_API SRTSOCKET srt_create_group(SRT_GROUP_TYPE); SRT_API SRTSOCKET srt_groupof(SRTSOCKET socket); -SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); +SRT_API SRTSTATUS srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); SRT_API SRT_SOCKOPT_CONFIG* srt_create_config(void); SRT_API void srt_delete_config(SRT_SOCKOPT_CONFIG* config /*nullable*/); -SRT_API int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); +SRT_API SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); SRT_API SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, const struct sockaddr* adr, int namelen); -SRT_API int srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); +SRT_API SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); #ifdef __cplusplus } diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 8a3e81323..ec61d399f 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -32,8 +32,8 @@ using namespace srt; extern "C" { -int srt_startup() { return CUDT::startup(); } -int srt_cleanup() { return CUDT::cleanup(); } +SRTRUNSTATUS srt_startup() { return CUDT::startup(); } +SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } // Socket creation. SRTSOCKET srt_socket(int , int , int ) { return CUDT::socket(); } @@ -43,7 +43,7 @@ SRTSOCKET srt_create_socket() { return CUDT::socket(); } // Group management. SRTSOCKET srt_create_group(SRT_GROUP_TYPE gt) { return CUDT::createGroup(gt); } SRTSOCKET srt_groupof(SRTSOCKET socket) { return CUDT::getGroupOfSocket(socket); } -int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen) +SRTSTATUS srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen) { return CUDT::getGroupData(socketgroup, output, inoutlen); } @@ -53,18 +53,18 @@ SRT_SOCKOPT_CONFIG* srt_create_config() return new SRT_SocketOptionObject; } -int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) { if (!config) - return -1; + return SRT_ERROR; if (!config->add(option, contents, len)) - return -1; + return SRT_ERROR; - return 0; + return SRT_STATUS_OK; } -int srt_connect_group(SRTSOCKET group, +SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize) { return CUDT::connectLinks(group, name, arraysize); @@ -74,11 +74,11 @@ int srt_connect_group(SRTSOCKET group, SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return SRT_INVALID_SOCK; } SRTSOCKET srt_groupof(SRTSOCKET) { return SRT_INVALID_SOCK; } -int srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } -int srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } -int srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSOCKET srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; } #endif @@ -90,7 +90,7 @@ SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struc #else data.errorcode = SRT_EINVOP; #endif - data.id = -1; + data.id = SRT_INVALID_SOCK; data.token = -1; data.weight = 0; data.config = NULL; @@ -112,23 +112,28 @@ void srt_delete_config(SRT_SOCKOPT_CONFIG* in) } // Binding and connection management -int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } -int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } +SRTSTATUS srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); } SRTSOCKET srt_accept_bond(const SRTSOCKET lsns[], int lsize, int64_t msTimeOut) { return CUDT::accept_bond(lsns, lsize, msTimeOut); } -int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } -int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } -int srt_connect_bind(SRTSOCKET u, +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int target_len) { return CUDT::connect(u, source, target, target_len); } -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { +#if ENABLE_BONDING + if (CUDT::isgroup(u)) + return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); +#endif + bool yes = 1; CUDT::setsockopt(u, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); @@ -138,14 +143,20 @@ int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_nam || local_name->sa_family != remote_name->sa_family) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); - const int st = srt_bind(u, local_name, local_namelen); - if (st != 0) + const SRTSTATUS st = srt_bind(u, local_name, local_namelen); + if (st != SRT_STATUS_OK) return st; - return srt_connect(u, remote_name, remote_namelen); + // Note: srt_connect may potentially return a socket value if it is used + // to connect a group. But rendezvous is not supported for groups. + const SRTSOCKET sst = srt_connect(u, remote_name, remote_namelen); + if (sst == SRT_INVALID_SOCK) + return SRT_ERROR; + + return SRT_STATUS_OK; } -int srt_close(SRTSOCKET u) +SRTSTATUS srt_close(SRTSOCKET u) { SRT_SOCKSTATUS st = srt_getsockstate(u); @@ -154,29 +165,27 @@ int srt_close(SRTSOCKET u) (st == SRTS_CLOSING) ) { // It's closed already. Do nothing. - return 0; + return SRT_STATUS_OK; } return CUDT::close(u); } -int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } -int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } -int srt_getsockdevname(SRTSOCKET u, char* devname, size_t * devnamelen) { return CUDT::getsockdevname(u, devname, devnamelen); } -int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) { return CUDT::getsockopt(u, level, optname, optval, optlen); } -int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) { return CUDT::setsockopt(u, level, optname, optval, optlen); } - -int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) +SRTSTATUS srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) { return CUDT::getsockopt(u, 0, opt, optval, optlen); } -int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } -int srt_getmaxpayloadsize(SRTSOCKET u) -{ - return CUDT::getMaxPayloadSize(u); -} +SRTSTATUS srt_getsockdevname(SRTSOCKET u, char* devname, size_t * devnamelen) +{ return CUDT::getsockdevname(u, devname, devnamelen); } + +int srt_getmaxpayloadsize(SRTSOCKET u) { return CUDT::getMaxPayloadSize(u); } int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } @@ -186,12 +195,12 @@ int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t siz { if (!path || !offset ) { - return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0).as(); } fstream ifs(path, ios::binary | ios::in); if (!ifs) { - return CUDT::APIError(MJ_FILESYSTEM, MN_READFAIL, 0); + return CUDT::APIError(MJ_FILESYSTEM, MN_READFAIL, 0).as(); } int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); ifs.close(); @@ -202,12 +211,12 @@ int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t siz { if (!path || !offset ) { - return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0).as(); } fstream ofs(path, ios::binary | ios::out); if (!ofs) { - return CUDT::APIError(MJ_FILESYSTEM, MN_WRAVAIL, 0); + return CUDT::APIError(MJ_FILESYSTEM, MN_WRAVAIL, 0).as(); } int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); ofs.close(); @@ -270,21 +279,21 @@ void srt_clearlasterror() UDT::getlasterror().clear(); } -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::getsockstate(u)); } // event mechanism int srt_epoll_create() { return CUDT::epoll_create(); } -int srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } +SRTSTATUS srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } // You can use either SRT_EPOLL_* flags or EPOLL* flags from , both are the same. IN/OUT/ERR only. // events == NULL accepted, in which case all flags are set. -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -298,15 +307,15 @@ int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) return CUDT::epoll_add_ssock(eid, s, &flag); } -int srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } -int srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_update_usock(eid, u, events); } -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -347,7 +356,7 @@ int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTim // Pass -1 to not change anything (but still get the current flag value). int32_t srt_epoll_set(int eid, int32_t flags) { return CUDT::epoll_set(eid, flags); } -int srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } +SRTSTATUS srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } void srt_setloglevel(int ll) { @@ -389,17 +398,17 @@ int srt_getrejectreason(SRTSOCKET sock) return CUDT::rejectReason(sock); } -int srt_setrejectreason(SRTSOCKET sock, int value) +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value) { return CUDT::rejectReason(sock, value); } -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return CUDT::installAcceptHook(lsn, hook, opaq); } -int srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return CUDT::installConnectHook(lsn, hook, opaq); } diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index d45215a01..5a71caed0 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -163,7 +163,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra m_bTsbPdWrapCheck = wrap; // Timebase passed here comes is calculated as: - // Tnow - hspkt.m_iTimeStamp + // Tnow - hspkt.timestamp() // where hspkt is the packet with SRT_CMD_HSREQ message. // // This function is called in the HSREQ reception handler only. diff --git a/srtcore/udt.h b/srtcore/udt.h index ee4c02f4d..5cc63aa41 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -166,22 +166,22 @@ typedef std::set UDSET; SRT_API extern const SRTSOCKET INVALID_SOCK; #undef ERROR -SRT_API extern const int ERROR; +SRT_API extern const SRTSTATUS ERROR; -SRT_API int startup(); -SRT_API int cleanup(); +SRT_API SRTRUNSTATUS startup(); +SRT_API SRTSTATUS cleanup(); SRT_API SRTSOCKET socket(); inline SRTSOCKET socket(int , int , int ) { return socket(); } -SRT_API int bind(SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int bind2(SRTSOCKET u, UDPSOCKET udpsock); -SRT_API int listen(SRTSOCKET u, int backlog); +SRT_API SRTSTATUS bind(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSTATUS bind2(SRTSOCKET u, UDPSOCKET udpsock); +SRT_API SRTSTATUS listen(SRTSOCKET u, int backlog); SRT_API SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen); -SRT_API int connect(SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int close(SRTSOCKET u); -SRT_API int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); -SRT_API int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); +SRT_API SRTSOCKET connect(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSTATUS close(SRTSOCKET u); +SRT_API SRTSTATUS getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); +SRT_API SRTSTATUS setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); SRT_API int send(SRTSOCKET u, const char* buf, int len, int flags); SRT_API int recv(SRTSOCKET u, char* buf, int len, int flags); @@ -200,22 +200,22 @@ SRT_API int selectEx(const std::vector& fds, std::vector* std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); SRT_API int epoll_create(); -SRT_API int epoll_add_usock(int eid, SRTSOCKET u, const int* events = NULL); -SRT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); -SRT_API int epoll_remove_usock(int eid, SRTSOCKET u); -SRT_API int epoll_remove_ssock(int eid, SYSSOCKET s); -SRT_API int epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); -SRT_API int epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL); +SRT_API SRTSTATUS epoll_add_usock(int eid, SRTSOCKET u, const int* events = NULL); +SRT_API SRTSTATUS epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); +SRT_API SRTSTATUS epoll_remove_usock(int eid, SRTSOCKET u); +SRT_API SRTSTATUS epoll_remove_ssock(int eid, SYSSOCKET s); +SRT_API SRTSTATUS epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); +SRT_API SRTSTATUS epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL); SRT_API int epoll_wait(int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); SRT_API int epoll_wait2(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut, SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL); SRT_API int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); -SRT_API int epoll_release(int eid); +SRT_API SRTSTATUS epoll_release(int eid); SRT_API ERRORINFO& getlasterror(); SRT_API int getlasterror_code(); SRT_API const char* getlasterror_desc(); -SRT_API int bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear = true); +SRT_API SRTSTATUS bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear = true); SRT_API SRT_SOCKSTATUS getsockstate(SRTSOCKET u); } // namespace UDT diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index c8cbc8722..cc0a13a31 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -90,25 +90,25 @@ class TestConnectionTimeout TEST_F(TestConnectionTimeout, Nonblocking) { const SRTSOCKET client_sock = srt_create_socket(); - ASSERT_GT(client_sock, 0); // socket_id should be > 0 + ASSERT_GT((int)client_sock, 0); // socket_id should be > 0 // First let's check the default connection timeout value. // It should be 3 seconds (3000 ms) int conn_timeout = 0; int conn_timeout_len = sizeof conn_timeout; - EXPECT_EQ(srt_getsockopt(client_sock, 0, SRTO_CONNTIMEO, &conn_timeout, &conn_timeout_len), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(client_sock, 0, SRTO_CONNTIMEO, &conn_timeout, &conn_timeout_len), SRT_STATUS_OK); EXPECT_EQ(conn_timeout, 3000); // Set connection timeout to 500 ms to reduce the test execution time const int connection_timeout_ms = 300; - EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); + EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_STATUS_OK); const int yes = 1; const int no = 0; - ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_SUCCESS); // for async connect - ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_SUCCESS); // for async connect - ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_SUCCESS); - ASSERT_EQ(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_SUCCESS); + ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_STATUS_OK); // for async connect + ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_STATUS_OK); // for async connect + ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_STATUS_OK); + ASSERT_EQ(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_STATUS_OK); const int pollid = srt_epoll_create(); ASSERT_GE(pollid, 0); @@ -116,7 +116,7 @@ TEST_F(TestConnectionTimeout, Nonblocking) { ASSERT_NE(srt_epoll_add_usock(pollid, client_sock, &epoll_out), SRT_ERROR); const sockaddr* psa = reinterpret_cast(&m_sa); - ASSERT_NE(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR); + ASSERT_NE(srt_connect(client_sock, psa, sizeof m_sa), SRT_INVALID_SOCK); // Socket readiness for connection is checked by polling on WRITE allowed sockets. { @@ -151,8 +151,8 @@ TEST_F(TestConnectionTimeout, Nonblocking) { EXPECT_EQ(write[0], client_sock); } - EXPECT_EQ(srt_epoll_remove_usock(pollid, client_sock), SRT_SUCCESS); - EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); + EXPECT_EQ(srt_epoll_remove_usock(pollid, client_sock), SRT_STATUS_OK); + EXPECT_EQ(srt_close(client_sock), SRT_STATUS_OK); (void)srt_epoll_release(pollid); } @@ -175,8 +175,8 @@ TEST_F(TestConnectionTimeout, BlockingLoop) for (int i = 0; i < 10; ++i) { const SRTSOCKET client_sock = srt_create_socket(); - EXPECT_GT(client_sock, 0); // socket_id should be > 0 - if (client_sock <= 0) + EXPECT_GT((int)client_sock, 0); // socket_id should be > 0 + if (int(client_sock) <= 0) break; // Set connection timeout to 999 ms to reduce the test execution time. @@ -184,10 +184,10 @@ TEST_F(TestConnectionTimeout, BlockingLoop) // 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_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_STATUS_OK); 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); + EXPECT_EQ(srt_connect(client_sock, psa, sizeof m_sa), SRT_INVALID_SOCK); const auto delta_ms = chrono::duration_cast(chrono::steady_clock::now() - chrono_ts_start).count(); // Confidence interval border : +/-200 ms @@ -203,7 +203,7 @@ TEST_F(TestConnectionTimeout, BlockingLoop) break; } - EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS); + EXPECT_EQ(srt_close(client_sock), SRT_STATUS_OK); } } diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 717b4549c..3a0fe173c 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -64,21 +64,21 @@ enum TEST_CASE struct TestResultNonBlocking { - int connect_ret; - int accept_ret; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; int epoll_wait_ret; int epoll_event; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state [CHECK_SOCKET_COUNT]; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state [CHECK_SOCKET_COUNT]; }; struct TestResultBlocking { - int connect_ret; - int accept_ret; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state[CHECK_SOCKET_COUNT]; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state[CHECK_SOCKET_COUNT]; }; @@ -137,35 +137,37 @@ static const std::string s_pwd_no(""); */ const int IGNORE_EPOLL = -2; -const int IGNORE_SRTS = -1; +const SRT_SOCKSTATUS IGNORE_SRTS = (SRT_SOCKSTATUS)-1; +const SRT_KM_STATE IGNORE_KMSTATE = (SRT_KM_STATE)-1; +const SRTSOCKET IGNORE_ACCEPT = (SRTSOCKET)-2; const TestCaseNonBlocking g_test_matrix_non_blocking[] = { - // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -185,31 +187,31 @@ const TestCaseNonBlocking g_test_matrix_non_blocking[] = */ const TestCaseBlocking g_test_matrix_blocking[] = { - // ENFORCEDENC | Password | | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -240,13 +242,13 @@ class TestEnforcedEncryption ASSERT_NE(m_caller_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_SENDER, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_caller_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); m_listener_socket = srt_create_socket(); ASSERT_NE(m_listener_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SENDER, &s_no, sizeof s_no), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_listener_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); // Will use this epoll to wait for srt_accept(...) const int epoll_out = SRT_EPOLL_IN | SRT_EPOLL_ERR; @@ -273,10 +275,10 @@ class TestEnforcedEncryption public: - int SetEnforcedEncryption(PEER_TYPE peer, bool value) + SRTSTATUS SetEnforcedEncryption(PEER_TYPE peer, bool value) { const SRTSOCKET &socket = peer == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); + return srt_setsockflag(socket, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); } @@ -285,15 +287,15 @@ class TestEnforcedEncryption const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; bool optval; int optlen = sizeof optval; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_STATUS_OK); return optval ? true : false; } - int SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) + SRTSTATUS SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) { const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); + return srt_setsockflag(socket, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); } @@ -301,8 +303,8 @@ class TestEnforcedEncryption { int km_state = 0; int opt_size = sizeof km_state; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_SUCCESS); - + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_STATUS_OK); + return km_state; } @@ -311,7 +313,7 @@ class TestEnforcedEncryption { int val = 0; int size = sizeof val; - EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_STATUS_OK); return val; } @@ -330,26 +332,26 @@ class TestEnforcedEncryption const bool is_blocking = std::is_same::value; if (is_blocking) { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); } else { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode } // Prepare input state const TestCase &test = GetTestMatrix(test_case); - ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_STATUS_OK); - ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_STATUS_OK); // Determine the subcase for the KLUDGE (check the behavior of the decryption failure) const bool case_pw_failure = test.password[PEER_CALLER] != test.password[PEER_LISTENER]; @@ -369,7 +371,7 @@ class TestEnforcedEncryption ASSERT_NE(srt_bind(m_listener_socket, psa, sizeof sa), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_socket, 4), SRT_ERROR); - SRTSOCKET accepted_socket = -1; + SRTSOCKET accepted_socket = SRT_INVALID_SOCK; auto accepting_thread = std::thread([&] { const int epoll_event = WaitOnEpoll(expect); @@ -398,12 +400,12 @@ class TestEnforcedEncryption std::cerr << "[T] ACCEPT SUCCEEDED: @" << accepted_socket << "\n"; } - EXPECT_NE(accepted_socket, 0); + EXPECT_NE(accepted_socket, SRT_SOCKID_CONNREQ); if (expect.accept_ret == SRT_INVALID_SOCK) { EXPECT_EQ(accepted_socket, SRT_INVALID_SOCK); } - else if (expect.accept_ret != -2) + else if (expect.accept_ret != IGNORE_ACCEPT) { EXPECT_NE(accepted_socket, SRT_INVALID_SOCK); } @@ -449,10 +451,10 @@ class TestEnforcedEncryption } }); - const int connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); + const SRTSOCKET connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); EXPECT_EQ(connect_ret, expect.connect_ret); - if (connect_ret == SRT_ERROR && connect_ret != expect.connect_ret) + if (connect_ret == SRT_INVALID_SOCK && connect_ret != expect.connect_ret) { std::cerr << "UNEXPECTED! srt_connect returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; @@ -524,7 +526,7 @@ class TestEnforcedEncryption std::cout << "W: " << epoll_res_w << std::endl; char buffer[1316] = {1, 2, 3, 4}; - ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), SRT_ERROR); + ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), int(SRT_ERROR)); std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -624,13 +626,13 @@ int TestEnforcedEncryption::WaitOnEpoll(const TestResultN } std::cerr << std::endl; - // Expect: -2 means that + // Expect: IGNORE_EPOLL means that you should not check the result. if (expect.epoll_wait_ret != IGNORE_EPOLL) { EXPECT_EQ(epoll_res, expect.epoll_wait_ret); } - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; return 0; @@ -696,8 +698,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) { #ifdef SRT_ENABLE_ENCRYPTION // Empty string sets password to none - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_STATUS_OK); EXPECT_EQ(SetPassword(PEER_CALLER, std::string("too_short")), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("too_short")), SRT_ERROR); @@ -715,8 +717,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) EXPECT_EQ(SetPassword(PEER_CALLER, long_pwd), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, long_pwd), SRT_ERROR); - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_STATUS_OK); #else EXPECT_EQ(SetPassword(PEER_CALLER, "whateverpassword"), SRT_ERROR); #endif @@ -732,8 +734,8 @@ TEST_F(TestEnforcedEncryption, SetGetDefault) EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), true); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), true); - EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_SUCCESS); - EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_SUCCESS); + EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_STATUS_OK); + EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_STATUS_OK); EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), false); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), false); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 742c37325..78e60f0f0 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -23,9 +23,9 @@ TEST(CEPoll, InfiniteWait) ASSERT_EQ(srt_epoll_wait(epoll_id, nullptr, nullptr, nullptr, nullptr, -1, - 0, 0, 0, 0), SRT_ERROR); + 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, WaitNoSocketsInEpoll) @@ -42,9 +42,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -57,9 +57,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -68,11 +68,11 @@ TEST(CEPoll, WaitEmptyCall) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -81,9 +81,9 @@ TEST(CEPoll, WaitEmptyCall) ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); ASSERT_EQ(srt_epoll_wait(epoll_id, 0, NULL, 0, NULL, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, UWaitEmptyCall) @@ -91,11 +91,11 @@ TEST(CEPoll, UWaitEmptyCall) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -103,9 +103,9 @@ TEST(CEPoll, UWaitEmptyCall) const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); - ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -114,14 +114,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -137,9 +137,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -148,14 +148,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -166,9 +166,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, WrongEpoll_idOnAddUSock) @@ -176,11 +176,11 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -189,7 +189,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) /* We intentionally pass the wrong socket ID. The error should be returned.*/ ASSERT_EQ(srt_epoll_add_usock(epoll_id + 1, client_sock, &epoll_out), SRT_ERROR); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -199,21 +199,21 @@ TEST(CEPoll, HandleEpollEvent) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -224,12 +224,12 @@ TEST(CEPoll, HandleEpollEvent) set* rval = &readset; set* wval = &writeset; - ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), SRT_ERROR); + ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), int(SRT_ERROR)); try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -239,7 +239,7 @@ TEST(CEPoll, HandleEpollEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -260,19 +260,19 @@ TEST(CEPoll, NotifyConnectionBreak) // 1. Prepare client MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes SRT_ATR_UNUSED = 1; const int no SRT_ATR_UNUSED = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int client_epoll_id = srt_epoll_create(); ASSERT_GE(client_epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; /* We intentionally pass the wrong socket ID. The error should be returned.*/ - EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_SUCCESS); + EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_STATUS_OK); sockaddr_in sa_client; memset(&sa_client, 0, sizeof sa_client); @@ -282,10 +282,10 @@ TEST(CEPoll, NotifyConnectionBreak) // 2. Prepare server MAKE_UNIQUE_SOCK(server_sock, "server_sock", srt_create_socket()); - ASSERT_NE(server_sock, SRT_ERROR); + ASSERT_NE(server_sock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int server_epoll_id = srt_epoll_create(); ASSERT_GE(server_epoll_id, 0); @@ -320,13 +320,13 @@ TEST(CEPoll, NotifyConnectionBreak) 0, 0, 0, 0); EXPECT_EQ(epoll_res, 1); - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; } // Wait for the caller connection thread to return connection result - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); sockaddr_in scl; int sclen = sizeof scl; @@ -343,15 +343,15 @@ TEST(CEPoll, NotifyConnectionBreak) this_thread::sleep_for(chrono::seconds(1)); cout << "(async call): Closing client connection\n"; return srt_close(client_sock); - }); + }); int timeout_ms = -1; - int ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; + SRTSOCKET ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; int len = 2; cout << "TEST: entering INFINITE WAIT\n"; const int epoll_wait_res = srt_epoll_wait(epoll_io, ready, &len, nullptr, nullptr, timeout_ms, 0, 0, 0, 0); cout << "TEST: return from INFINITE WAIT\n"; - if (epoll_wait_res == SRT_ERROR) + if (epoll_wait_res == int(SRT_ERROR)) cerr << "socket::read::epoll " << to_string(srt_getlasterror(nullptr)); EXPECT_EQ(epoll_wait_res, 1); EXPECT_EQ(len, 1); @@ -359,7 +359,7 @@ TEST(CEPoll, NotifyConnectionBreak) // Wait for the caller to close connection // There should be no wait, as epoll should wait until connection is closed. - EXPECT_EQ(close_res.get(), SRT_SUCCESS); + EXPECT_EQ(close_res.get(), SRT_STATUS_OK); const SRT_SOCKSTATUS state = srt_getsockstate(sock); const bool state_valid = state == SRTS_BROKEN || state == SRTS_CLOSING || state == SRTS_CLOSED; EXPECT_TRUE(state_valid); @@ -374,21 +374,21 @@ TEST(CEPoll, HandleEpollEvent2) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR | SRT_EPOLL_ET; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -409,7 +409,7 @@ TEST(CEPoll, HandleEpollEvent2) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -419,7 +419,7 @@ TEST(CEPoll, HandleEpollEvent2) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -435,21 +435,21 @@ TEST(CEPoll, HandleEpollNoEvent) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); SRT_EPOLL_EVENT fds[1024]; @@ -460,7 +460,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -470,7 +470,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -485,11 +485,11 @@ TEST(CEPoll, ThreadedUpdate) srt::TestInit srtinit; MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect CEPoll epoll; const int epoll_id = epoll.create(); @@ -502,7 +502,7 @@ TEST(CEPoll, ThreadedUpdate) this_thread::sleep_for(chrono::seconds(1)); // Make sure that uwait will be called as first cerr << "ADDING sockets to eid\n"; const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -526,7 +526,7 @@ TEST(CEPoll, ThreadedUpdate) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -536,7 +536,7 @@ TEST(CEPoll, ThreadedUpdate) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -953,8 +953,8 @@ class TestEPoll: public srt::Test { protected: - int m_client_pollid = SRT_ERROR; - SRTSOCKET m_client_sock = SRT_ERROR; + int m_client_pollid = int(SRT_ERROR); + SRTSOCKET m_client_sock = SRT_INVALID_SOCK; void clientSocket() { @@ -962,12 +962,12 @@ class TestEPoll: public srt::Test int no = 0; m_client_sock = srt_create_socket(); - ASSERT_NE(m_client_sock, SRT_ERROR); + ASSERT_NE(m_client_sock, SRT_INVALID_SOCK); - 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_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); + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_out = SRT_EPOLL_OUT; srt_epoll_add_usock(m_client_pollid, m_client_sock, &epoll_out); @@ -981,7 +981,7 @@ class TestEPoll: public srt::Test sockaddr* psa = (sockaddr*)&sa; - ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_ERROR); + ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_INVALID_SOCK); // Socket readiness for connection is checked by polling on WRITE allowed sockets. @@ -997,7 +997,7 @@ class TestEPoll: public srt::Test 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); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 0); // get exactly one write event without reads ASSERT_EQ(wlen, 1); // get exactly one write event without reads @@ -1009,11 +1009,11 @@ class TestEPoll: public srt::Test -1, // infinit ttl true // in order must be set to true ), - SRT_ERROR); + int(SRT_ERROR)); // disable receiving OUT events int epoll_err = SRT_EPOLL_ERR; - ASSERT_EQ(0, srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err)); + ASSERT_EQ(srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err), SRT_STATUS_OK); { int rlen = 2; SRTSOCKET read[2]; @@ -1026,11 +1026,11 @@ class TestEPoll: public srt::Test 1000, 0, 0, 0, 0)); const int last_error = srt_getlasterror(NULL); - EXPECT_EQ(SRT_ETIMEOUT, last_error) << last_error; + EXPECT_EQ(last_error, (int)SRT_ETIMEOUT) << last_error; } } - int m_server_pollid = SRT_ERROR; + int m_server_pollid = int(SRT_ERROR); void createServerSocket(SRTSOCKET& w_servsock) { @@ -1038,10 +1038,10 @@ class TestEPoll: public srt::Test int no = 0; SRTSOCKET servsock = srt_create_socket(); - ASSERT_NE(servsock, SRT_ERROR); + ASSERT_NE(servsock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(servsock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(servsock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_in = SRT_EPOLL_IN; srt_epoll_add_usock(m_server_pollid, servsock, &epoll_in); @@ -1075,7 +1075,7 @@ class TestEPoll: public srt::Test 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 ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -1102,7 +1102,7 @@ class TestEPoll: public srt::Test 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 ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -1140,10 +1140,10 @@ class TestEPoll: public srt::Test void setup() override { m_client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_client_pollid); + ASSERT_NE(m_client_pollid, int(SRT_ERROR)); m_server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_server_pollid); + ASSERT_NE(m_server_pollid, int(SRT_ERROR)); } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index e7b27fb1b..97d7d0228 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -313,8 +313,8 @@ TEST(TestFEC, Connection) // that 1s might not be enough. SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); - ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + ASSERT_NE(a, SRT_INVALID_SOCK); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -372,7 +372,7 @@ TEST(TestFEC, ConnectionReorder) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -430,7 +430,7 @@ TEST(TestFEC, ConnectionFull1) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -488,7 +488,7 @@ TEST(TestFEC, ConnectionFull2) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -546,7 +546,7 @@ TEST(TestFEC, ConnectionMess) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR) << srt_getlasterror_str(); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -602,7 +602,7 @@ TEST(TestFEC, ConnectionForced) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 40f299e73..6ad97072a 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -53,21 +53,22 @@ TEST(FileTransmission, Upload) // Find unused a port not used by any other service. // Otherwise srt_connect may actually connect. - int bind_res = -1; + SRTSTATUS bind_res = SRT_ERROR; 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) + int bind_err = srt_getlasterror(NULL); + if (bind_res == SRT_STATUS_OK) { 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_TRUE(bind_err == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_err; } - ASSERT_GE(bind_res, 0); + ASSERT_GE((int)bind_res, 0); srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 9843a0303..1f1c2027c 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -34,12 +34,12 @@ class TestIPv6 void setup() override { m_caller_sock = srt_create_socket(); - ASSERT_NE(m_caller_sock, SRT_ERROR); + ASSERT_NE(m_caller_sock, SRT_INVALID_SOCK); // IPv6 calling IPv4 would otherwise fail if the system-default net.ipv6.bindv6only=1. ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_IPV6ONLY, &no, sizeof no), SRT_ERROR); m_listener_sock = srt_create_socket(); - ASSERT_NE(m_listener_sock, SRT_ERROR); + ASSERT_NE(m_listener_sock, SRT_INVALID_SOCK); m_CallerStarted.reset(new std::promise); m_ReadyCaller.reset(new std::promise); diff --git a/test/test_main.cpp b/test/test_main.cpp index e2243a306..2079e2c36 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -94,12 +94,12 @@ bool TestEnv::Allowed_IPv6() void TestInit::start(int& w_retstatus) { - ASSERT_GE(w_retstatus = srt_startup(), 0); + ASSERT_GE(w_retstatus = (int)srt_startup(), 0); } void TestInit::stop() { - EXPECT_NE(srt_cleanup(), -1); + EXPECT_NE((int)srt_cleanup(), -1); } // This function finds some interesting options among command diff --git a/testing/srt-test-file.cpp b/testing/srt-test-file.cpp index 3dd8fe998..baacc66d1 100644 --- a/testing/srt-test-file.cpp +++ b/testing/srt-test-file.cpp @@ -261,7 +261,7 @@ bool DoUpload(UriParser& ut, string path, string filename) { int st = srt_send(ss, buf.data()+shift, int(n)); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; return false; @@ -290,7 +290,7 @@ bool DoUpload(UriParser& ut, string path, string filename) size_t bytes; size_t blocks; int st = srt_getsndbuffer(ss, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; return false; @@ -358,7 +358,7 @@ bool DoDownload(UriParser& us, string directory, string filename) for (;;) { int n = srt_recv(ss, buf.data(), int(::g_buffer_size)); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; return false; diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 25401f0ff..4dfc9eccf 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -299,7 +299,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr { SRT_GROUP_TYPE gt; size = sizeof gt; - if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) + if (SRT_ERROR != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { if (size_t(gt) < Size(gtypes)) Verb(" type=", gtypes[gt], VerbNoEOL); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 679756e1d..1114f4a6d 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -539,10 +539,10 @@ void SrtCommon::InitParameters(string host, string path, map par) void SrtCommon::PrepareListener(string host, int port, int backlog) { m_bindsock = srt_create_socket(); - if (m_bindsock == SRT_ERROR) + if (m_bindsock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_bindsock); + SRTSTATUS stat = ConfigurePre(m_bindsock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -597,7 +597,7 @@ void SrtCommon::AcceptNewClient() int len = 2; SRTSOCKET ready[2]; - while (srt_epoll_wait(srt_conn_epoll, ready, &len, 0, 0, 1000, 0, 0, 0, 0) == -1) + while (srt_epoll_wait(srt_conn_epoll, ready, &len, 0, 0, 1000, 0, 0, 0, 0) == int(SRT_ERROR)) { if (::transmit_int_state) Error("srt_epoll_wait for srt_accept: interrupt"); @@ -620,7 +620,7 @@ void SrtCommon::AcceptNewClient() } int maxsize = srt_getmaxpayloadsize(m_sock); - if (maxsize == SRT_ERROR) + if (maxsize == int(SRT_ERROR)) { srt_close(m_bindsock); srt_close(m_sock); @@ -635,7 +635,7 @@ void SrtCommon::AcceptNewClient() } #if ENABLE_BONDING - if (m_sock & SRTGROUP_MASK) + if (int32_t(m_sock) & SRTGROUP_MASK) { m_listener_group = true; if (m_group_config != "") @@ -648,7 +648,7 @@ void SrtCommon::AcceptNewClient() #ifndef SRT_OLD_APP_READER - if (srt_epoll != -1) + if (srt_epoll != int(SRT_ERROR)) { Verb() << "(Group: erasing epoll " << srt_epoll << ") " << VerbNoEOL; srt_epoll_release(srt_epoll); @@ -671,19 +671,19 @@ void SrtCommon::AcceptNewClient() { sockaddr_any peeraddr(AF_INET6); string peer = ""; - if (-1 != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) + if (SRT_ERROR != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) { peer = peeraddr.str(); } sockaddr_any agentaddr(AF_INET6); string agent = "", dev = ""; - if (-1 != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) + if (SRT_ERROR != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) { agent = agentaddr.str(); char name[256]; size_t len = 255; - if (srt_getsockdevname(m_sock, name, &len) == SRT_SUCCESS) + if (srt_getsockdevname(m_sock, name, &len) == SRT_STATUS_OK) dev.assign(name, len); } @@ -693,7 +693,7 @@ void SrtCommon::AcceptNewClient() // ConfigurePre is done on bindsock, so any possible Pre flags // are DERIVED by sock. ConfigurePost is done exclusively on sock. - int stat = ConfigurePost(m_sock); + SRTSTATUS stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) Error("ConfigurePost"); } @@ -818,7 +818,7 @@ void SrtCommon::Init(string host, int port, string path, map par, if (!m_blocking_mode) { // Don't add new epoll if already created as a part - // of group management: if (srt_epoll == -1)... + // of group management: if (srt_epoll == SRT_ERROR)... if (m_mode == "caller") dir = (dir | SRT_EPOLL_UPDATE); @@ -831,7 +831,7 @@ void SrtCommon::Init(string host, int port, string path, map par, int SrtCommon::AddPoller(SRTSOCKET socket, int modes) { int pollid = srt_epoll_create(); - if (pollid == -1) + if (pollid == int(SRT_ERROR)) throw std::runtime_error("Can't create epoll in nonblocking mode"); Verb() << "EPOLL: creating eid=" << pollid << " and adding @" << socket << " in " << DirectionName(SRT_EPOLL_OPT(modes)) << " mode"; @@ -839,15 +839,15 @@ int SrtCommon::AddPoller(SRTSOCKET socket, int modes) return pollid; } -int SrtCommon::ConfigurePost(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePost(SRTSOCKET sock) { bool yes = m_blocking_mode; - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; if (m_direction & SRT_EPOLL_OUT) { Verb() << "Setting SND blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -858,7 +858,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) if (m_timeout) result = srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -872,7 +872,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) { Verb() << "Setting RCV blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; if (m_timeout) @@ -882,7 +882,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) int timeout = 1000; result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout); } - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -903,18 +903,18 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) } } - return 0; + return SRT_STATUS_OK; } -int SrtCommon::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePre(SRTSOCKET sock) { - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; int no = 0; if (!m_tsbpdmode) { result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no); - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -922,7 +922,7 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) // This is for asynchronous connect. int maybe = m_blocking_mode; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &maybe, sizeof maybe); - if (result == -1) + if (result == SRT_ERROR) return result; // host is only checked for emptiness and depending on that the connection mode is selected. @@ -946,14 +946,14 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } void SrtCommon::SetupAdapter(const string& host, int port) { Verb() << "Binding the caller socket to " << host << ":" << port << " ..."; auto lsa = CreateAddr(host, port); - int stat = srt_bind(m_sock, lsa.get(), sizeof lsa); + SRTSTATUS stat = srt_bind(m_sock, lsa.get(), sizeof lsa); if (stat == SRT_ERROR) Error("srt_bind"); } @@ -973,10 +973,10 @@ void SrtCommon::OpenClient(string host, int port) void SrtCommon::PrepareClient() { m_sock = srt_create_socket(); - if (m_sock == SRT_ERROR) + if (m_sock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_sock); + SRTSTATUS stat = ConfigurePre(m_sock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -1067,12 +1067,12 @@ void SrtCommon::OpenGroupClient() } m_sock = srt_create_group(type); - if (m_sock == -1) + if (m_sock == SRT_INVALID_SOCK) Error("srt_create_group"); srt_connect_callback(m_sock, &TransmitGroupSocketConnect, this); - int stat = -1; + SRTSTATUS stat = SRT_ERROR; if (m_group_config != "") { Verb() << "Ignoring setting group config: '" << m_group_config; @@ -1080,7 +1080,7 @@ void SrtCommon::OpenGroupClient() stat = ConfigurePre(m_sock); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) Error("ConfigurePre"); if (!m_blocking_mode) @@ -1148,9 +1148,9 @@ void SrtCommon::OpenGroupClient() Connect_Again: Verb() << "Waiting for group connection... " << VerbNoEOL; - int fisock = srt_connect_group(m_sock, targets.data(), int(targets.size())); + SRTSOCKET fisock = srt_connect_group(m_sock, targets.data(), int(targets.size())); - if (fisock == SRT_ERROR) + if (fisock == SRT_INVALID_SOCK) { // Complete the error information for every member ostringstream out; @@ -1205,7 +1205,7 @@ void SrtCommon::OpenGroupClient() // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[j].id != -1 && targets[j].errorcode == SRT_SUCCESS) + if (targets[j].id != SRT_INVALID_SOCK && targets[j].errorcode == SRT_SUCCESS) { m_group_nodes[j].socket = targets[j].id; } @@ -1215,14 +1215,14 @@ void SrtCommon::OpenGroupClient() // should be added to epoll. size_t size = m_group_data.size(); stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); stat = srt_group_data(m_sock, m_group_data.data(), &size); } - if (stat == -1) + if (stat == SRT_ERROR) { Error("srt_group_data"); } @@ -1231,7 +1231,7 @@ void SrtCommon::OpenGroupClient() for (size_t j = 0; j < m_group_nodes.size(); ++j) { SRTSOCKET insock = m_group_nodes[j].socket; - if (insock == -1) + if (insock == SRT_INVALID_SOCK) { Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; continue; @@ -1260,7 +1260,7 @@ void SrtCommon::OpenGroupClient() ready_conn, &len1, -1, // Wait infinitely NULL, NULL, - NULL, NULL) != -1) + NULL, NULL) != int(SRT_ERROR)) { Verb() << "[C]" << VerbNoEOL; for (int ii = 0; ii < len1; ++ii) @@ -1319,7 +1319,7 @@ void SrtCommon::OpenGroupClient() } stat = ConfigurePost(m_sock); - if (stat == -1) + if (stat == SRT_ERROR) { // This kind of error must reject the whole operation. // Usually you'll get this error on the first socket, @@ -1407,13 +1407,13 @@ void SrtCommon::ConnectClient(string host, int port) srt_connect_callback(m_sock, &TransmitConnectCallback, 0); } - int stat = -1; + SRTSTATUS stat = SRT_ERROR; for (;;) { ::transmit_throw_on_interrupt = true; - stat = srt_connect(m_sock, sa.get(), sizeof sa); + SRTSOCKET stats = srt_connect(m_sock, sa.get(), sizeof sa); ::transmit_throw_on_interrupt = false; - if (stat == SRT_ERROR) + if (stats == SRT_INVALID_SOCK) { int reason = srt_getrejectreason(m_sock); #if PLEASE_LOG @@ -1446,7 +1446,7 @@ void SrtCommon::ConnectClient(string host, int port) // Socket readiness for connection is checked by polling on WRITE allowed sockets. int lenc = 2, lene = 2; SRTSOCKET ready_connect[2], ready_error[2]; - if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != -1) + if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != (int)SRT_ERROR) { // We should have just one socket, so check whatever socket // is in the transmit_error_storage. @@ -1488,7 +1488,7 @@ void SrtCommon::ConnectClient(string host, int port) } int maxsize = srt_getmaxpayloadsize(m_sock); - if (maxsize == SRT_ERROR) + if (maxsize == int(SRT_ERROR)) { srt_close(m_sock); Error("srt_getmaxpayloadsize"); @@ -1509,7 +1509,7 @@ void SrtCommon::ConnectClient(string host, int port) srt_getsockname(m_sock, agent.get(), &agent.len); char name[256]; size_t len = 255; - if (srt_getsockdevname(m_sock, name, &len) == SRT_SUCCESS) + if (srt_getsockdevname(m_sock, name, &len) == SRT_STATUS_OK) dev.assign(name, len); } Verb("Connected AGENT:", agent.str(), "[", dev, "] PEER:", sa.str()); @@ -1571,7 +1571,7 @@ void SrtCommon::SetupRendezvous(string adapter, string host, int port) if (target.family() == AF_INET6) showhost = "[" + showhost + "]"; Verb() << "Binding rendezvous: " << showhost << ":" << outport << " ..."; - int stat = srt_bind(m_sock, localsa.get(), localsa.size()); + SRTSTATUS stat = srt_bind(m_sock, localsa.get(), localsa.size()); if (stat == SRT_ERROR) { srt_close(m_sock); @@ -1645,10 +1645,10 @@ void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpda SRTSOCKET id = d.id; SRT_SOCKSTATUS status = d.sockstate; - int result = d.result; + SRTSTATUS result = d.result; SRT_MEMBERSTATUS mstatus = d.memberstate; - if (result != -1 && status == SRTS_CONNECTED) + if (result != SRT_ERROR && status == SRTS_CONNECTED) { // Short report with the state. Verb() << "G@" << id << "<" << MemberStatusStr(mstatus) << "> " << VerbNoEOL; @@ -1693,8 +1693,8 @@ void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpda gd.config = n.options; gd.token = n.token; - int fisock = srt_connect_group(m_sock, &gd, 1); - if (fisock == SRT_ERROR) + SRTSOCKET fisock = srt_connect_group(m_sock, &gd, 1); + if (fisock == SRT_INVALID_SOCK) { // Whatever. Skip the node. Verb() << "FAILED: "; @@ -1794,7 +1794,7 @@ bytevector SrtSource::GroupRead(size_t chunk) size_t size = m_group_data.size(); int stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); @@ -1806,7 +1806,7 @@ bytevector SrtSource::GroupRead(size_t chunk) m_group_data.resize(size); } - if (stat == -1) // Also after the above fix + if (stat == SRT_ERROR) // Also after the above fix { Error(UDT::getlasterror(), "FAILURE when reading group data"); } @@ -1842,7 +1842,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // Check first the ahead packets if you have any to deliver. - if (m_group_seqno != -1 && !m_group_positions.empty()) + if (m_group_seqno != SRT_SEQNO_NONE && !m_group_positions.empty()) { bytevector ahead_packet; @@ -2097,7 +2097,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // NOTE: checks against m_group_seqno and decisions based on it - // must NOT be done if m_group_seqno is -1, which means that we + // must NOT be done if m_group_seqno is NONE, which means that we // are about to deliver the very first packet and we take its // sequence number as a good deal. @@ -2107,11 +2107,11 @@ bytevector SrtSource::GroupRead(size_t chunk) // - check ordering. // The second one must be done always, but failed discrepancy // check should exclude the socket from any further checks. - // That's why the common check for m_group_seqno != -1 can't + // That's why the common check for m_group_seqno != NONE can't // embrace everything below. // We need to first qualify the sequence, just for a case - if (m_group_seqno != -1 && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) + if (m_group_seqno != SRT_SEQNO_NONE && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) { // This error should be returned if the link turns out // to be the only one, or set to the group data. @@ -2139,7 +2139,7 @@ bytevector SrtSource::GroupRead(size_t chunk) p->sequence = mctrl.pktseq; } - if (m_group_seqno != -1) + if (m_group_seqno != SRT_SEQNO_NONE) { // Now we can safely check it. int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_group_seqno); @@ -2228,7 +2228,7 @@ bytevector SrtSource::GroupRead(size_t chunk) // Update it now and don't do anything else with the sockets. // Sanity check - if (next_seq == -1) + if (next_seq == SRT_SEQNO_NONE) { Error("IPE: next_seq not set after output extracted!"); } @@ -2432,7 +2432,7 @@ MediaPacket SrtSource::Read(size_t chunk) ::transmit_throw_on_interrupt = true; stat = srt_recvmsg2(m_sock, data.data(), int(chunk), &mctrl); ::transmit_throw_on_interrupt = false; - if (stat != SRT_ERROR) + if (stat != int(SRT_ERROR)) { ready = true; } @@ -2452,13 +2452,13 @@ MediaPacket SrtSource::Read(size_t chunk) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != int(SRT_ERROR)) { Verb() << "... epoll reported ready " << len << " sockets"; // If the event was SRT_EPOLL_UPDATE, report it, and still wait. bool any_read_ready = false; - vector errored; + vector errored; for (int i = 0; i < len; ++i) { if (sready[i].events & SRT_EPOLL_UPDATE) @@ -2552,10 +2552,10 @@ SrtTarget::SrtTarget(std::string host, int port, std::string path, const std::ma } -int SrtTarget::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtTarget::ConfigurePre(SRTSOCKET sock) { - int result = SrtCommon::ConfigurePre(sock); - if (result == -1) + SRTSTATUS result = SrtCommon::ConfigurePre(sock); + if (result == SRT_ERROR) return result; int yes = 1; @@ -2564,10 +2564,10 @@ int SrtTarget::ConfigurePre(SRTSOCKET sock) // In HSv4 this setting is obligatory; otherwise the SRT handshake // extension will not be done at all. result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; - return 0; + return SRT_STATUS_OK; } void SrtTarget::Write(const MediaPacket& data) @@ -2583,7 +2583,7 @@ void SrtTarget::Write(const MediaPacket& data) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != int(SRT_ERROR)) { bool any_write_ready = false; for (int i = 0; i < len; ++i) @@ -2632,7 +2632,7 @@ void SrtTarget::Write(const MediaPacket& data) // if ALL links from the group have failed to perform // the operation. If only one did, the result will be // visible in the status array. - if (stat == SRT_ERROR) + if (stat == int(SRT_ERROR)) Error("srt_sendmsg"); ::transmit_throw_on_interrupt = false; diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index f44e9a768..c723e5699 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -142,7 +142,7 @@ class SrtCommon void Acquire(SRTSOCKET s) { m_sock = s; - if (s & SRTGROUP_MASK) + if (int32_t(s) & SRTGROUP_MASK) m_listener_group = true; } @@ -153,8 +153,8 @@ class SrtCommon void Error(std::string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); void Init(std::string host, int port, std::string path, std::map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); - virtual int ConfigurePost(SRTSOCKET sock); - virtual int ConfigurePre(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePost(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePre(SRTSOCKET sock); void OpenClient(std::string host, int port); #if ENABLE_BONDING @@ -226,7 +226,7 @@ class SrtTarget: public virtual Target, public virtual SrtCommon SrtTarget(std::string host, int port, std::string path, const std::map& par); SrtTarget() {} - int ConfigurePre(SRTSOCKET sock) override; + SRTSTATUS ConfigurePre(SRTSOCKET sock) override; void Write(const MediaPacket& data) override; bool IsOpen() override { return IsUsable(); } bool Broken() override { return IsBroken(); } @@ -249,7 +249,7 @@ class SrtRelay: public Relay, public SrtSource, public SrtTarget SrtRelay(std::string host, int port, std::string path, const std::map& par); SrtRelay() {} - int ConfigurePre(SRTSOCKET sock) override + SRTSTATUS ConfigurePre(SRTSOCKET sock) override { // This overrides the change introduced in SrtTarget, // which sets the SRTO_SENDER flag. For a bidirectional transmission From 9746d3492bf4efb034f88ca4f2ee9d776ed6d197 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 19 Mar 2025 16:57:27 +0100 Subject: [PATCH 162/174] [MAINT] Removed compile option dependencies in the API. (#3146) Co-authored-by: Mikolaj Malecki --- apps/socketoptions.hpp | 16 +- srtcore/common.cpp | 2 - srtcore/common.h | 2 - srtcore/socketconfig.cpp | 22 +- srtcore/srt.h | 50 ++- srtcore/srt_c_api.cpp | 18 +- testing/srt-test-live.cpp | 37 +- testing/testmedia.cpp | 730 +------------------------------------- testing/testmedia.hpp | 23 -- 9 files changed, 78 insertions(+), 822 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index e6de4db17..1ffb2febb 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -74,13 +74,11 @@ inline int SocketOption::setso(SRTSOCKET socket, i return (int)srt_setsockflag(socket, SRT_SOCKOPT(sym), data, (int) size); } -#if ENABLE_BONDING template<> inline int SocketOption::setso(SRT_SOCKOPT_CONFIG* obj, int /*ignored*/, int sym, const void* data, size_t size) { return (int)srt_config_add(obj, SRT_SOCKOPT(sym), data, (int) size); } -#endif template<> @@ -251,20 +249,12 @@ const SocketOption srt_options [] { { "ipv6only", 0, SRTO_IPV6ONLY, SocketOption::PRE, SocketOption::INT, nullptr }, { "peeridletimeo", 0, SRTO_PEERIDLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr }, { "packetfilter", 0, SRTO_PACKETFILTER, SocketOption::PRE, SocketOption::STRING, nullptr }, -#if ENABLE_BONDING { "groupconnect", 0, SRTO_GROUPCONNECT, SocketOption::PRE, SocketOption::INT, nullptr}, { "groupminstabletimeo", 0, SRTO_GROUPMINSTABLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, -#endif -#ifdef SRT_ENABLE_BINDTODEVICE { "bindtodevice", 0, SRTO_BINDTODEVICE, SocketOption::PRE, SocketOption::STRING, nullptr}, -#endif - { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr } -#ifdef ENABLE_AEAD_API_PREVIEW - ,{ "cryptomode", 0, SRTO_CRYPTOMODE, SocketOption::PRE, SocketOption::INT, nullptr } -#endif -#ifdef ENABLE_MAXREXMITBW - ,{ "maxrexmitbw", 0, SRTO_MAXREXMITBW, SocketOption::POST, SocketOption::INT64, nullptr } -#endif + { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr }, + { "cryptomode", 0, SRTO_CRYPTOMODE, SocketOption::PRE, SocketOption::INT, nullptr }, + { "maxrexmitbw", 0, SRTO_MAXREXMITBW, SocketOption::POST, SocketOption::INT64, nullptr } }; } diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 696a4ba5a..9c4ddf18d 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -633,7 +633,6 @@ std::string SockStatusStr(SRT_SOCKSTATUS s) return names.names[int(s)-1]; } -#if ENABLE_BONDING std::string MemberStatusStr(SRT_MEMBERSTATUS s) { if (int(s) < int(SRT_GST_PENDING) || int(s) > int(SRT_GST_BROKEN)) @@ -656,7 +655,6 @@ std::string MemberStatusStr(SRT_MEMBERSTATUS s) return names.names[int(s)]; } -#endif } // (end namespace srt_logging) diff --git a/srtcore/common.h b/srtcore/common.h index d9dea08d4..cda6f935b 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -130,9 +130,7 @@ struct CNetworkInterface namespace srt_logging { std::string SockStatusStr(SRT_SOCKSTATUS s); -#if ENABLE_BONDING std::string MemberStatusStr(SRT_MEMBERSTATUS s); -#endif } namespace srt diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 56a7c73e6..b4909495c 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -912,7 +912,7 @@ struct CSrtConfigSetter } }; -#ifdef ENABLE_AEAD_API_PREVIEW +#if defined(ENABLE_AEAD_API_PREVIEW) && defined(SRT_ENABLE_ENCRYPTION) template<> struct CSrtConfigSetter { @@ -920,7 +920,6 @@ struct CSrtConfigSetter { using namespace srt_logging; const int val = cast_optval(optval, optlen); -#ifdef SRT_ENABLE_ENCRYPTION if (val < CSrtConfig::CIPHER_MODE_AUTO || val > CSrtConfig::CIPHER_MODE_AES_GCM) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -937,11 +936,22 @@ struct CSrtConfigSetter } co.iCryptoMode = val; + + } +}; +#else +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& , const void* , int ) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + LOGC(aclog.Error, log << "SRT was built without AEAD enabled."); #else LOGC(aclog.Error, log << "SRT was built without crypto module."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); #endif - + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } }; #endif @@ -1002,9 +1012,7 @@ int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int opt DISPATCH(SRTO_IPV6ONLY); DISPATCH(SRTO_PACKETFILTER); DISPATCH(SRTO_RETRANSMITALGO); -#ifdef ENABLE_AEAD_API_PREVIEW - DISPATCH(SRTO_CRYPTOMODE); -#endif + DISPATCH(SRTO_CRYPTOMODE); // STUB if not supported #ifdef ENABLE_MAXREXMITBW DISPATCH(SRTO_MAXREXMITBW); #endif diff --git a/srtcore/srt.h b/srtcore/srt.h index 4151caad7..b37a94b28 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -16,6 +16,12 @@ written by #ifndef INC_SRTC_H #define INC_SRTC_H +// Behavior-controlling macros that can be set in the command line: +// +// * SRT_DYNAMIC: Define when creating a dynamic library on Windows +// * SRT_EXPORTS: Define when compiling SRT as dynamic on Windows +// * SRT_NO_DEPRECATED: Disable warnings for deprecated API + #ifndef SRT_API #ifdef _WIN32 #ifdef SRT_DYNAMIC @@ -53,10 +59,19 @@ written by // You can use these constants with SRTO_MINVERSION option. #define SRT_VERSION_FEAT_HSv5 0x010300 -#if defined(__cplusplus) && __cplusplus > 201406 +#ifdef __cplusplus + +#if __cplusplus > 201406L #define SRT_HAVE_CXX17 1 +#define SRT_HAVE_CXX11 1 +#elif __cplusplus > 199711L +#define SRT_HAVE_CXX17 0 +#define SRT_HAVE_CXX11 1 #else #define SRT_HAVE_CXX17 0 +#define SRT_HAVE_CXX11 0 +#endif + #endif @@ -162,10 +177,6 @@ static const int32_t SRTGROUP_MASK = (1 << 30); typedef int SYSSOCKET; #endif -#ifndef ENABLE_BONDING -#define ENABLE_BONDING 0 -#endif - typedef SYSSOCKET UDPSOCKET; @@ -247,12 +258,8 @@ typedef enum SRT_SOCKOPT { SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake (ENABLE_BONDING) SRTO_PACKETFILTER = 60, // Add and configure a packet filter SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm -#ifdef ENABLE_AEAD_API_PREVIEW SRTO_CRYPTOMODE = 62, // Encryption cipher mode (AES-CTR, AES-GCM, ...). -#endif -#ifdef ENABLE_MAXREXMITBW SRTO_MAXREXMITBW = 63, // Maximum bandwidth limit for retransmision (Bytes/s) -#endif SRTO_E_SIZE // Always last element, not a valid option. } SRT_SOCKOPT; @@ -260,33 +267,29 @@ typedef enum SRT_SOCKOPT { #ifdef __cplusplus - -#if __cplusplus > 199711L // C++11 +#if SRT_HAVE_CXX11 // Newer compilers report error when [[deprecated]] is applied to types, // and C++11 and higher uses this. // Note that this doesn't exactly use the 'deprecated' attribute, // as it's introduced in C++14. What is actually used here is the // fact that unknown attributes are ignored, but still warned about. // This should only catch an eye - and that's what it does. -#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT [[deprecated]])value) + #define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT [[deprecated]])value) #else // Older (pre-C++11) compilers use gcc deprecated applied to a typedef typedef SRT_ATR_DEPRECATED_PX SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED SRT_ATR_DEPRECATED; -#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value) + #define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value) #endif - -#else +#else // C version // deprecated enum labels are supported only since gcc 6, so in C there // will be a whole deprecated enum type, as it's not an error in C to mix // enum types enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED { - // Dummy last option, as every entry ends with a comma SRTO_DEPRECATED_END = 0 - }; #define SRT_DEPRECATED_OPTION(value) ((enum SRT_SOCKOPT_DEPRECATED)value) #endif @@ -295,6 +298,7 @@ enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED // stays so that it can be used in future. Example: // #define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53) +// Values used for SRTO_TRANSTYPE option typedef enum SRT_TRANSTYPE { SRTT_LIVE, @@ -581,18 +585,12 @@ enum SRT_REJECT_REASON SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group SRT_REJ_TIMEOUT = 16,// connection timeout -#ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations -#endif SRT_REJ_CONFIG = 18, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; -// XXX This value remains for some time, but it's deprecated -// Planned deprecation removal: rel1.6.0. -#define SRT_REJ__SIZE SRT_REJ_E_SIZE - // Reject category codes: #define SRT_REJC_VALUE(code) (1000 * (code/1000)) @@ -670,10 +668,8 @@ enum SRT_KM_STATE SRT_KM_S_SECURING = 1, // Stream encrypted, exchanging Keying Material SRT_KM_S_SECURED = 2, // Stream encrypted, keying Material exchanged, decrypting ok. SRT_KM_S_NOSECRET = 3, // Stream encrypted and no secret to decrypt Keying Material - SRT_KM_S_BADSECRET = 4 // Stream encrypted and wrong secret is used, cannot decrypt Keying Material -#ifdef ENABLE_AEAD_API_PREVIEW - ,SRT_KM_S_BADCRYPTOMODE = 5 // Stream encrypted but wrong cryptographic mode is used, cannot decrypt. Since v1.5.2. -#endif + SRT_KM_S_BADSECRET = 4, // Stream encrypted and wrong secret is used, cannot decrypt Keying Material + SRT_KM_S_BADCRYPTOMODE = 5 // Stream encrypted but wrong cryptographic mode is used, cannot decrypt. Since v1.5.2. }; enum SRT_EPOLL_OPT diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index ec61d399f..0e5f15c4b 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -39,7 +39,7 @@ SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } SRTSOCKET srt_socket(int , int , int ) { return CUDT::socket(); } SRTSOCKET srt_create_socket() { return CUDT::socket(); } -#if ENABLE_BONDING +#if defined(ENABLE_BONDING) && ENABLE_BONDING == 1 // Group management. SRTSOCKET srt_create_group(SRT_GROUP_TYPE gt) { return CUDT::createGroup(gt); } SRTSOCKET srt_groupof(SRTSOCKET socket) { return CUDT::getGroupOfSocket(socket); } @@ -70,16 +70,23 @@ SRTSOCKET srt_connect_group(SRTSOCKET group, return CUDT::connectLinks(group, name, arraysize); } +void srt_delete_config(SRT_SOCKOPT_CONFIG* in) +{ + delete in; +} + #else -SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return SRT_INVALID_SOCK; } -SRTSOCKET srt_groupof(SRTSOCKET) { return SRT_INVALID_SOCK; } +SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL), SRT_INVALID_SOCK; } +SRTSOCKET srt_groupof(SRTSOCKET) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL), SRT_INVALID_SOCK; } SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } SRTSOCKET srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; } +void srt_delete_config(SRT_SOCKOPT_CONFIG*) { } + #endif SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struct sockaddr* dst, int namelen) @@ -106,11 +113,6 @@ SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struc return data; } -void srt_delete_config(SRT_SOCKOPT_CONFIG* in) -{ - delete in; -} - // Binding and connection management SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 4dfc9eccf..82656fc69 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -79,6 +79,11 @@ #include #include +// Define as 1 to test how the stubbed non-bonding version is working. +#ifndef ENABLE_BONDING +#define ENABLE_BONDING 0 +#endif + using namespace std; srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-live"); @@ -280,7 +285,6 @@ namespace srt_logging extern Logger glog; } -#if ENABLE_BONDING extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr*, const char* ) { static string gtypes[] = { @@ -311,7 +315,6 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr return 0; } -#endif extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const sockaddr*, const char* streamid) { @@ -419,6 +422,8 @@ int main( int argc, char** argv ) o_hook ((optargs), " Use listener callback of given specification (internally coded)", "hook"), #if ENABLE_BONDING o_group ((optargs), " Using multiple SRT connections as redundancy group", "g"), +#else + o_group ((optargs), " NOT SUPPORTED (Bonding not enabled at compile time)", "g"), #endif o_stime ((optargs), " Pass source time explicitly to SRT output", "st", "srctime", "sourcetime"), o_retry ((optargs), " Retry connection N times if failed on timeout", "rc", "retry"), @@ -432,25 +437,23 @@ int main( int argc, char** argv ) vector args = params[""]; string source_spec, target_spec; -#if ENABLE_BONDING vector groupspec = Option(params, vector{}, o_group); -#endif vector source_items, target_items; if (!need_help) { // You may still need help. -#if ENABLE_BONDING - if ( !groupspec.empty() ) + if (!groupspec.empty()) { +#if ENABLE_BONDING // Check if you have something before -g and after -g. if (args.empty()) { // Then all items are sources, but the last one is a single target. if (groupspec.size() < 3) { - cerr << "ERROR: Redundancy group: with nothing preceding -g, use -g ... (at least 3 args)\n"; + cerr << "ERROR: Bonding group: with nothing preceding -g, use -g ... (at least 3 args)\n"; need_help = true; } else @@ -465,9 +468,12 @@ int main( int argc, char** argv ) copy(args.begin(), args.end(), back_inserter(source_items)); copy(groupspec.begin(), groupspec.end(), back_inserter(target_items)); } +#else + cerr << "ERROR: Option: -g: Bonding feature not enabled at compile time\n"; + return 1; +#endif } else -#endif { if (args.size() < 2) { @@ -489,7 +495,7 @@ int main( int argc, char** argv ) unique_ptr pout_verb; int verbch = 1; // default cerr - if (verbose_val != "no") + if (!need_help && verbose_val != "no") { Verbose::on = true; if (verbose_val == "") @@ -530,10 +536,9 @@ int main( int argc, char** argv ) } } - if (!need_help) { - // Redundancy is then simply recognized by the fact that there are + // Bonding is then simply recognized by the fact that there are // multiple specified inputs or outputs, for SRT caller only. Check // every URI in advance. if (!CheckMediaSpec("INPUT", source_items, (source_spec))) @@ -582,16 +587,24 @@ int main( int argc, char** argv ) // Unrecognized helpspec is same as no helpspec, that is, general help. cerr << "Usage:\n"; +#if ENABLE_BONDING cerr << " (1) " << argv[0] << " [options] \n"; cerr << " (2) " << argv[0] << " -g [options]\n"; +#else + cerr << " " << argv[0] << " [options] \n"; +#endif cerr << "*** (Position of [options] is unrestricted.)\n"; cerr << "*** ( option parameters can be only terminated by a next option.)\n"; +#if ENABLE_BONDING cerr << "where:\n"; cerr << " (1) Exactly one input and one output URI spec is required,\n"; cerr << " (2) Multiple SRT inputs or output as redundant links are allowed.\n"; cerr << " `URI1 URI2 -g URI3` uses 1, 2 input and 3 output\n"; cerr << " `-g URI1 URI2 URI3` like above\n"; cerr << " `URI1 -g URI2 URI3` uses 1 input and 2, 3 output\n"; +#else + cerr << "*** (Exactly one input and one output should be specified)\n"; +#endif cerr << "SUPPORTED URI SCHEMES:\n"; cerr << " srt: use SRT connection\n"; cerr << " udp: read from bound UDP socket or send to given address as UDP\n"; @@ -648,13 +661,11 @@ int main( int argc, char** argv ) transmit_accept_hook_op = (void*)&g_reject_data; transmit_accept_hook_fn = &SrtRejectByCodeHook; } -#if ENABLE_BONDING else if (hargs[0] == "groupcheck") { transmit_accept_hook_fn = &SrtCheckGroupHook; transmit_accept_hook_op = nullptr; } -#endif } string pfextra; diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 1114f4a6d..b2b9c332c 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -45,9 +45,7 @@ using namespace srt; using srt_logging::KmStateStr; using srt_logging::SockStatusStr; -#if ENABLE_BONDING using srt_logging::MemberStatusStr; -#endif srt::sync::atomic transmit_throw_on_interrupt {false}; srt::sync::atomic transmit_int_state {false}; @@ -232,7 +230,6 @@ void SrtCommon::InitParameters(string host, string path, map par) path = path.substr(2); -#if ENABLE_BONDING if (path == "group") { // Group specified, check type. @@ -372,7 +369,6 @@ void SrtCommon::InitParameters(string host, string path, map par) // possible in future. par["mode"] = "caller"; } -#endif } if (par.count("bind")) @@ -634,7 +630,6 @@ void SrtCommon::AcceptNewClient() Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); } -#if ENABLE_BONDING if (int32_t(m_sock) & SRTGROUP_MASK) { m_listener_group = true; @@ -646,7 +641,6 @@ void SrtCommon::AcceptNewClient() // There might be added a poller, remove it. // We need it work different way. -#ifndef SRT_OLD_APP_READER if (srt_epoll != int(SRT_ERROR)) { @@ -657,7 +651,6 @@ void SrtCommon::AcceptNewClient() // Don't add any sockets, they will have to be added // anew every time again. srt_epoll = srt_epoll_create(); -#endif // Group data must have a size of at least 1 // otherwise the srt_group_data() call will fail @@ -667,7 +660,6 @@ void SrtCommon::AcceptNewClient() Verb() << " connected(group epoll " << srt_epoll <<")."; } else -#endif { sockaddr_any peeraddr(AF_INET6); string peer = ""; @@ -749,12 +741,10 @@ void SrtCommon::Init(string host, int port, string path, map par, { OpenClient(host, port); } -#if ENABLE_BONDING else { OpenGroupClient(); // Source data are in the fields already. } -#endif } else if (m_mode == "listener") OpenServer(m_adapter, port, backlog); @@ -987,7 +977,6 @@ void SrtCommon::PrepareClient() } -#if ENABLE_BONDING void TransmitGroupSocketConnect(void* srtcommon, SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token) { SrtCommon* that = (SrtCommon*)srtcommon; @@ -1340,35 +1329,6 @@ void SrtCommon::OpenGroupClient() // Prepare group data for monitoring the group status. m_group_data.resize(m_group_nodes.size()); } -#endif - -/* - This may be used sometimes for testing, but it's nonportable. - void SrtCommon::SpinWaitAsync() - { - static string udt_status_names [] = { - "INIT" , "OPENED", "LISTENING", "CONNECTING", "CONNECTED", "BROKEN", "CLOSING", "CLOSED", "NONEXIST" - }; - - for (;;) - { - SRT_SOCKSTATUS state = srt_getsockstate(m_sock); - if (int(state) < SRTS_CONNECTED) - { - if (Verbose::on) - Verb() << state; - usleep(250000); - continue; - } - else if (int(state) > SRTS_CONNECTED) - { - Error("UDT::connect status=" + udt_status_names[state]); - } - - return; - } - } - */ struct TransmitErrorReason { @@ -1613,7 +1573,6 @@ SrtCommon::~SrtCommon() Close(); } -#if ENABLE_BONDING void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size) { if (!grpdata) @@ -1706,7 +1665,6 @@ void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpda } } } -#endif SrtSource::SrtSource(string host, int port, std::string path, const map& par) { @@ -1728,669 +1686,6 @@ static void PrintSrtStats(SRTSOCKET sock, bool clr, bool bw, bool stats) cout << transmit_stats_writer->WriteStats(sock, perf); } - -#ifdef SRT_OLD_APP_READER - -// NOTE: 'output' is expected to be EMPTY here. -bool SrtSource::GroupCheckPacketAhead(bytevector& output) -{ - bool status = false; - vector past_ahead; - - // This map no longer maps only ahead links. - // Here are all links, and whether ahead, it's defined by the sequence. - for (auto i = m_group_positions.begin(); i != m_group_positions.end(); ++i) - { - // i->first: socket ID - // i->second: ReadPos { sequence, packet } - // We are not interested with the socket ID because we - // aren't going to read from it - we have the packet already. - ReadPos& a = i->second; - - int seqdiff = CSeqNo::seqcmp(a.sequence, m_group_seqno); - if ( seqdiff == 1) - { - // The very next packet. Return it. - m_group_seqno = a.sequence; - Verb() << " (SRT group: ahead delivery %" << a.sequence << " from @" << i->first << ")"; - swap(output, a.packet); - status = true; - } - else if (seqdiff < 1 && !a.packet.empty()) - { - Verb() << " (@" << i->first << " dropping collected ahead %" << a.sequence << ")"; - a.packet.clear(); - } - // In case when it's >1, keep it in ahead - } - - return status; -} - -static string DisplayEpollResults(const std::set& sockset, std::string prefix) -{ - typedef set fset_t; - ostringstream os; - os << prefix << " "; - for (fset_t::const_iterator i = sockset.begin(); i != sockset.end(); ++i) - { - os << "@" << *i << " "; - } - - return os.str(); -} - -bytevector SrtSource::GroupRead(size_t chunk) -{ - // Read the current group status. m_sock is here the group id. - bytevector output; - - // 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; - -RETRY_READING: - - size_t size = m_group_data.size(); - int stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == SRT_ERROR && size > m_group_data.size()) - { - // Just too small buffer. Resize and continue. - m_group_data.resize(size); - stat = srt_group_data(m_sock, m_group_data.data(), &size); - } - else - { - // Downsize if needed. - m_group_data.resize(size); - } - - if (stat == SRT_ERROR) // Also after the above fix - { - Error(UDT::getlasterror(), "FAILURE when reading group data"); - } - - if (size == 0) - { - Error("No sockets in the group - disconnected"); - } - - bool connected = false; - for (auto& d: m_group_data) - { - if (d.status == SRTS_CONNECTED) - { - connected = true; - break; - } - } - if (!connected) - { - Error("All sockets in the group disconnected"); - } - - if (Verbose::on) - { - for (auto& d: m_group_data) - { - if (d.status != SRTS_CONNECTED) - // id, status, result, peeraddr - Verb() << "@" << d.id << " <" << SockStatusStr(d.status) << "> (=" << d.result << ") PEER:" - << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str(); - } - } - - // Check first the ahead packets if you have any to deliver. - if (m_group_seqno != SRT_SEQNO_NONE && !m_group_positions.empty()) - { - bytevector ahead_packet; - - // This function also updates the group sequence pointer. - if (GroupCheckPacketAhead(ahead_packet)) - return move(ahead_packet); - } - - // LINK QUALIFICATION NAMES: - // - // HORSE: Correct link, which delivers the very next sequence. - // Not necessarily this link is currently active. - // - // KANGAROO: Got some packets dropped and the sequence number - // of the packet jumps over the very next sequence and delivers - // an ahead packet. - // - // ELEPHANT: Is not ready to read, while others are, or reading - // up to the current latest delivery sequence number does not - // reach this sequence and the link becomes non-readable earlier. - - // The above condition has ruled out one kangaroo and turned it - // into a horse. - - // Below there's a loop that will try to extract packets. Kangaroos - // will be among the polled ones because skipping them risks that - // the elephants will take over the reading. Links already known as - // elephants will be also polled in an attempt to revitalize the - // connection that experienced just a short living choking. - // - // After polling we attempt to read from every link that reported - // read-readiness and read at most up to the sequence equal to the - // current delivery sequence. - - // Links that deliver a packet below that sequence will be retried - // until they deliver no more packets or deliver the packet of - // expected sequence. Links that don't have a record in m_group_positions - // and report readiness will be always read, at least to know what - // sequence they currently stand on. - // - // Links that are already known as kangaroos will be polled, but - // no reading attempt will be done. If after the reading series - // it will turn out that we have no more horses, the slowest kangaroo - // will be "advanced to a horse" (the ahead link with a sequence - // closest to the current delivery sequence will get its sequence - // set as current delivered and its recorded ahead packet returned - // as the read packet). - - // If we find at least one horse, the packet read from that link - // will be delivered. All other link will be just ensured update - // up to this sequence number, or at worst all available packets - // will be read. In this case all kangaroos remain kangaroos, - // until the current delivery sequence m_group_seqno will be lifted - // to the sequence recorded for these links in m_group_positions, - // during the next time ahead check, after which they will become - // horses. - - Verb() << "E(" << srt_epoll << ") " << VerbNoEOL; - - for (size_t i = 0; i < size; ++i) - { - SRT_SOCKGROUPDATA& d = m_group_data[i]; - if (d.status == SRTS_CONNECTING) - { - Verb() << "@" << d.id << " " << VerbNoEOL; - int modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - srt_epoll_add_usock(srt_epoll, d.id, &modes); - continue; // don't read over a failed or pending socket - } - - if (d.status >= SRTS_BROKEN) - { - broken.insert(d.id); - } - - if (broken.count(d.id)) - { - Verb() << "@" << d.id << " " << VerbNoEOL; - continue; - } - - if (d.status != SRTS_CONNECTED) - { - Verb() << "@" << d.id << " " << VerbNoEOL; - // Sockets in this state are ignored. We are waiting until it - // achieves CONNECTING state, then it's added to write. - 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 - // omitting kangaroos will result in only elephants to be polled for - // reading. Elephants, 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 also that about the fact that some links turn out to be - // elephants we'll learn only after we try to poll and read them. - - // Note that d.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. - - int modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; - srt_epoll_add_usock(srt_epoll, d.id, &modes); - Verb() << "@" << d.id << "[READ] " << VerbNoEOL; - } - - Verb() << ""; - - // 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. - - SrtPollState sready; - - // Poll on this descriptor until reading is available, indefinitely. - if (UDT::epoll_swait(srt_epoll, sready, -1) == SRT_ERROR) - { - Error(UDT::getlasterror(), "UDT::epoll_swait(srt_epoll, group)"); - } - if (Verbose::on) - { - Verb() << "RDY: {" - << DisplayEpollResults(sready.rd(), "[R]") - << DisplayEpollResults(sready.wr(), "[W]") - << DisplayEpollResults(sready.ex(), "[E]") - << "} " << VerbNoEOL; - - } - - LOGC(applog.Debug, log << "epoll_swait: " - << DisplayEpollResults(sready.rd(), "[R]") - << DisplayEpollResults(sready.wr(), "[W]") - << DisplayEpollResults(sready.ex(), "[E]")); - - typedef set fset_t; - - // Handle sockets of pending connection and with errors. - broken = sready.ex(); - - // We don't do anything about sockets that have been configured to - // poll on writing (that is, pending for connection). What we need - // is that the epoll_swait call exit on that fact. Probably if this - // was the only socket reported, no broken and no read-ready, this - // will later check on output if still empty, if so, repeat the whole - // function. This write-ready socket will be there already in the - // connected state and will be added to read-polling. - - // 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, - // we'll worry later about adjusting it to the current group sequence - // position. - // 2. If a socket is already position ahead, DO NOT read from it, even - // if it is ready. - - // The state of things whether we were able to extract the very next - // sequence will be simply defined by the fact that `output` is nonempty. - - int32_t next_seq = m_group_seqno; - - // 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. - for (fset_t::const_iterator i = sready.rd().begin(); i != sready.rd().end(); ++i) - { - // Check if this socket is in aheads - // If so, don't read from it, wait until the ahead is flushed. - - SRTSOCKET id = *i; - ReadPos* p = nullptr; - auto pe = m_group_positions.find(id); - if (pe != m_group_positions.end()) - { - p = &pe->second; - // Possible results of comparison: - // x < 0: the sequence is in the past, the socket should be adjusted FIRST - // 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->sequence, m_group_seqno); - if (seqdiff > 1) - { - Verb() << "EPOLL: @" << id << " %" << p->sequence << " AHEAD, not reading."; - continue; - } - } - - - // Read from this socket stubbornly, until: - // - reading is no longer possible (AGAIN) - // - the sequence difference is >= 1 - - int fi = 1; // marker for Verb to display flushing - for (;;) - { - bytevector data(chunk); - SRT_MSGCTRL mctrl = srt_msgctrl_default; - stat = srt_recvmsg2(id, data.data(), chunk, &mctrl); - if (stat == SRT_ERROR) - { - if (fi == 0) - { - if (Verbose::on) - { - if (p) - { - int32_t pktseq = p->sequence; - int seqdiff = CSeqNo::seqcmp(p->sequence, m_group_seqno); - Verb() << ". %" << pktseq << " " << seqdiff << ")"; - } - else - { - Verb() << ".)"; - } - } - fi = 1; - } - int err = srt_getlasterror(0); - if (err == SRT_EASYNCRCV) - { - // Do not treat this as spurious, just stop reading. - break; - } - Verb() << "Error @" << id << ": " << srt_getlasterror_str(); - broken.insert(id); - break; - } - - // NOTE: checks against m_group_seqno and decisions based on it - // must NOT be done if m_group_seqno is NONE, which means that we - // are about to deliver the very first packet and we take its - // sequence number as a good deal. - - // The order must be: - // - check discrepancy - // - record the sequence - // - check ordering. - // The second one must be done always, but failed discrepancy - // check should exclude the socket from any further checks. - // That's why the common check for m_group_seqno != NONE can't - // embrace everything below. - - // We need to first qualify the sequence, just for a case - if (m_group_seqno != SRT_SEQNO_NONE && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) - { - // This error should be returned if the link turns out - // to be the only one, or set to the group data. - // err = SRT_ESECFAIL; - if (fi == 0) - { - Verb() << ".)"; - fi = 1; - } - Verb() << "Error @" << id << ": SEQUENCE DISCREPANCY: base=%" << m_group_seqno << " vs pkt=%" << mctrl.pktseq << ", setting ESECFAIL"; - broken.insert(id); - break; - } - - // Rewrite it to the state for a case when next reading - // would not succeed. Do not insert the buffer here because - // this is only required when the sequence is ahead; for that - // it will be fixed later. - if (!p) - { - p = &(m_group_positions[id] = ReadPos { mctrl.pktseq, {} }); - } - else - { - p->sequence = mctrl.pktseq; - } - - if (m_group_seqno != SRT_SEQNO_NONE) - { - // Now we can safely check it. - int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_group_seqno); - - if (seqdiff <= 0) - { - if (fi == 1) - { - Verb() << "(@" << id << " FLUSH:" << VerbNoEOL; - fi = 0; - } - - Verb() << "." << VerbNoEOL; - - // The sequence is recorded, the packet has to be discarded. - // That's all. - continue; - } - - // Finish flush reporting if fallen into here - if (fi == 0) - { - Verb() << ". %" << mctrl.pktseq << " " << (-seqdiff) << ")"; - fi = 1; - } - - // Now we have only two possibilities: - // seqdiff == 1: The very next sequence, we want to read and return the packet. - // seqdiff > 1: The packet is ahead - record the ahead packet, but continue with the others. - - if (seqdiff > 1) - { - Verb() << "@" << id << " %" << mctrl.pktseq << " AHEAD"; - p->packet = move(data); - break; // Don't read from that socket anymore. - } - } - - // We have seqdiff = 1, or we simply have the very first packet - // which's sequence is taken as a good deal. Update the sequence - // and record output. - - if (!output.empty()) - { - Verb() << "@" << id << " %" << mctrl.pktseq << " REDUNDANT"; - break; - } - - - Verb() << "@" << id << " %" << mctrl.pktseq << " DELIVERING"; - output = move(data); - - // Record, but do not update yet, until all sockets are handled. - next_seq = mctrl.pktseq; - break; - } - } - - // ready_len is only the length of currently reported - // ready sockets, NOT NECESSARILY containing all sockets from the group. - if (broken.size() == size) - { - // All broken - Error("All sockets broken"); - } - - if (Verbose::on && !broken.empty()) - { - Verb() << "BROKEN: " << Printable(broken) << " - removing"; - } - - // Now remove all broken sockets from aheads, if any. - // Even if they have already delivered a packet. - for (SRTSOCKET d: broken) - { - m_group_positions.erase(d); - srt_close(d); - } - - // May be required to be re-read. - broken.clear(); - - if (!output.empty()) - { - // We have extracted something, meaning that we have the sequence shift. - // Update it now and don't do anything else with the sockets. - - // Sanity check - if (next_seq == SRT_SEQNO_NONE) - { - Error("IPE: next_seq not set after output extracted!"); - } - m_group_seqno = next_seq; - return output; - } - - // Check if we have any sockets left :D - - // Here we surely don't have any more HORSES, - // only ELEPHANTS and KANGAROOS. Qualify them and - // attempt to at least take advantage of KANGAROOS. - - // In this position all links are either: - // - updated to the current position - // - updated to the newest possible position available - // - not yet ready for extraction (not present in the group) - - // If we haven't extracted the very next sequence position, - // it means that we might only have the ahead packets read, - // that is, the next sequence has been dropped by all links. - - if (!m_group_positions.empty()) - { - // This might notify both lingering links, which didn't - // deliver the required sequence yet, and links that have - // the sequence ahead. Review them, and if you find at - // least one packet behind, just wait for it to be ready. - // Use again the waiting function because we don't want - // the general waiting procedure to skip others. - set elephants; - - // const because it's `typename decltype(m_group_positions)::value_type` - pair* slowest_kangaroo = nullptr; - - for (auto& sock_rp: m_group_positions) - { - // NOTE that m_group_seqno in this place wasn't updated - // because we haven't successfully extracted anything. - int seqdiff = CSeqNo::seqcmp(sock_rp.second.sequence, m_group_seqno); - if (seqdiff < 0) - { - elephants.insert(sock_rp.first); - } - // If seqdiff == 0, we have a socket ON TRACK. - else if (seqdiff > 0) - { - if (!slowest_kangaroo) - { - slowest_kangaroo = &sock_rp; - } - else - { - // Update to find the slowest kangaroo. - int seqdiff = CSeqNo::seqcmp(slowest_kangaroo->second.sequence, sock_rp.second.sequence); - if (seqdiff > 0) - { - slowest_kangaroo = &sock_rp; - } - } - } - } - - // Note that if no "slowest_kangaroo" was found, it means - // that we don't have kangaroos. - if (slowest_kangaroo) - { - // We have a slowest kangaroo. Elephants must be ignored. - // Best case, they will get revived, worst case they will be - // soon broken. - // - // As we already have the packet delivered by the slowest - // kangaroo, we can simply return it. - - m_group_seqno = slowest_kangaroo->second.sequence; - Verb() << "@" << slowest_kangaroo->first << " %" << m_group_seqno << " KANGAROO->HORSE"; - swap(output, slowest_kangaroo->second.packet); - return output; - } - - // Here ALL LINKS ARE ELEPHANTS, stating that we still have any. - if (Verbose::on) - { - if (!elephants.empty()) - { - // If we don't have kangaroos, then simply reattempt to - // poll all elephants again anyway (at worst they are all - // broken and we'll learn about it soon). - Verb() << "ALL LINKS ELEPHANTS. Re-polling."; - } - else - { - Verb() << "ONLY BROKEN WERE REPORTED. Re-polling."; - } - } - goto RETRY_READING; - } - - // We have checked so far only links that were ready to poll. - // Links that are not ready should be re-checked. - // Links that were not ready at the entrance should be checked - // separately, and probably here is the best moment to do it. - // After we make sure that at least one link is ready, we can - // reattempt to read a packet from it. - - // Ok, so first collect all sockets that are in - // connecting state, make a poll for connection. - srt_epoll_clear_usocks(srt_epoll); - bool have_connectors = false, have_ready = false; - for (auto& d: m_group_data) - { - if (d.status < SRTS_CONNECTED) - { - // Not sure anymore if IN or OUT signals the connect-readiness, - // but no matter. The signal will be cleared once it is used, - // while it will be always on when there's anything ready to read. - int modes = SRT_EPOLL_IN | SRT_EPOLL_OUT; - srt_epoll_add_usock(srt_epoll, d.id, &modes); - have_connectors = true; - } - else if (d.status == SRTS_CONNECTED) - { - have_ready = true; - } - } - - if (have_ready || have_connectors) - { - Verb() << "(still have: " << (have_ready ? "+" : "-") << "ready, " - << (have_connectors ? "+" : "-") << "conenctors)."; - goto RETRY_READING; - } - - if (have_ready) - { - Verb() << "(connected in the meantime)"; - // Some have connected in the meantime, don't - // waste time on the pending ones. - goto RETRY_READING; - } - - if (have_connectors) - { - Verb() << "(waiting for pending connectors to connect)"; - // Wait here for them to be connected. - vector sready; - sready.resize(m_group_data.size()); - int ready_len = m_group_data.size(); - if (srt_epoll_wait(srt_epoll, sready.data(), &ready_len, 0, 0, -1, 0, 0, 0, 0) == SRT_ERROR) - { - Error("All sockets in the group disconnected"); - } - - goto RETRY_READING; - } - - Error("No data extracted"); - return output; // Just a marker - this above function throws an exception -} - -#endif - MediaPacket SrtSource::Read(size_t chunk) { static size_t counter = 1; @@ -2398,19 +1693,6 @@ MediaPacket SrtSource::Read(size_t chunk) bool have_group SRT_ATR_UNUSED = !m_group_nodes.empty(); bytevector data(chunk); - // EXPERIMENTAL -#ifdef SRT_OLD_APP_READER - if (have_group || m_listener_group) - { - data = GroupRead(chunk); - } - - if (have_group) - { - // This is to be done for caller mode only - UpdateGroupStatus(m_group_data.data(), m_group_data.size()); - } -#else SRT_MSGCTRL mctrl = srt_msgctrl_default; bool ready = true; @@ -2418,13 +1700,11 @@ MediaPacket SrtSource::Read(size_t chunk) do { -#if ENABLE_BONDING if (have_group || m_listener_group) { mctrl.grpdata = m_group_data.data(); mctrl.grpdata_size = m_group_data.size(); } -#endif if (::transmit_int_state) Error("srt_recvmsg2: interrupted"); @@ -2520,7 +1800,6 @@ MediaPacket SrtSource::Read(size_t chunk) const bool need_bw_report = transmit_bw_report && int(counter % transmit_bw_report) == transmit_bw_report - 1; const bool need_stats_report = transmit_stats_report && counter % transmit_stats_report == transmit_stats_report - 1; -#if ENABLE_BONDING if (have_group) // Means, group with caller mode { UpdateGroupStatus(mctrl.grpdata, mctrl.grpdata_size); @@ -2532,14 +1811,12 @@ MediaPacket SrtSource::Read(size_t chunk) } } else -#endif { if (transmit_stats_writer && (need_stats_report || need_bw_report)) { PrintSrtStats(m_sock, need_stats_report, need_bw_report, need_stats_report); } } -#endif ++counter; @@ -2558,6 +1835,9 @@ SRTSTATUS SrtTarget::ConfigurePre(SRTSOCKET sock) if (result == SRT_ERROR) return result; + if (sock & SRTGROUP_MASK) + return 0; + int yes = 1; // This is for the HSv4 compatibility; if both parties are HSv5 // (min. version 1.2.1), then this setting simply does nothing. @@ -2612,14 +1892,12 @@ void SrtTarget::Write(const MediaPacket& data) } SRT_MSGCTRL mctrl = srt_msgctrl_default; -#if ENABLE_BONDING bool have_group = !m_group_nodes.empty(); if (have_group || m_listener_group) { mctrl.grpdata = m_group_data.data(); mctrl.grpdata_size = m_group_data.size(); } -#endif if (transmit_use_sourcetime) { @@ -2639,7 +1917,6 @@ void SrtTarget::Write(const MediaPacket& data) const bool need_bw_report = transmit_bw_report && int(counter % transmit_bw_report) == transmit_bw_report - 1; const bool need_stats_report = transmit_stats_report && counter % transmit_stats_report == transmit_stats_report - 1; -#if ENABLE_BONDING if (have_group) { // For listener group this is not necessary. The group information @@ -2653,7 +1930,6 @@ void SrtTarget::Write(const MediaPacket& data) } } else -#endif { if (transmit_stats_writer && (need_stats_report || need_bw_report)) { diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index c723e5699..9bb71f2d0 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -69,28 +69,22 @@ class SrtCommon struct Connection: ConnectionBase { -#if ENABLE_BONDING SRT_SOCKOPT_CONFIG* options = nullptr; -#endif int error = SRT_SUCCESS; int reason = SRT_REJ_UNKNOWN; Connection(std::string h, int p): ConnectionBase(h, p) {} Connection(Connection&& old): ConnectionBase(old) { -#if ENABLE_BONDING if (old.options) { options = old.options; old.options = nullptr; } -#endif } ~Connection() { -#if ENABLE_BONDING srt_delete_config(options); -#endif } }; @@ -107,20 +101,7 @@ class SrtCommon std::vector m_group_nodes; std::string m_group_type; std::string m_group_config; -#if ENABLE_BONDING std::vector m_group_data; -#ifdef SRT_OLD_APP_READER - int32_t m_group_seqno = -1; - - struct ReadPos - { - int32_t sequence; - bytevector packet; - }; - std::map m_group_positions; - SRTSOCKET m_group_active; // The link from which the last packet was delivered -#endif -#endif SRTSOCKET m_sock = SRT_INVALID_SOCK; SRTSOCKET m_bindsock = SRT_INVALID_SOCK; @@ -157,9 +138,7 @@ class SrtCommon virtual SRTSTATUS ConfigurePre(SRTSOCKET sock); void OpenClient(std::string host, int port); -#if ENABLE_BONDING void OpenGroupClient(); -#endif void PrepareClient(); void SetupAdapter(const std::string& host, int port); void ConnectClient(std::string host, int port); @@ -199,8 +178,6 @@ class SrtSource: public virtual Source, public virtual SrtCommon MediaPacket Read(size_t chunk) override; bytevector GroupRead(size_t chunk); - bool GroupCheckPacketAhead(bytevector& output); - /* In this form this isn't needed. From d971dd434c1f1c621c6b01de7ef6f72b4ba724be Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 20 Mar 2025 15:14:42 +0100 Subject: [PATCH 163/174] [DOC] Hotfix: #2585 already done through another PR, removed irrelevant documentation entry --- docs/API/API-functions.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 447ac4f99..d74681ebc 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -2980,10 +2980,6 @@ For other values below `SRT_REJC_PREDEFINED` it returns the string for [`SRT_REJ_UNKNOWN`](#SRT_REJ_UNKNOWN). For values since `SRT_REJC_PREDEFINED` on, returns "Application-defined rejection reason". -The actual messages assigned to the internal rejection codes, that is, less than -`SRT_REJ_E_SIZE`, can be also obtained from the `srt_rejectreason_msg` array. - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- From 27dd2bc047c62f22e5bd58e0e111e73caa56c20a Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 20 Mar 2025 17:02:46 +0100 Subject: [PATCH 164/174] Logic refax: new rules for the receiver buffer and TSBPD (#2527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added gap management to receiver buffer (marks the end of ICR and first-to-drop) * Reworked the waking points for TSBPD thread to only changeable actions --------- Co-authored-by: Mikołaj Małecki Co-authored-by: Maxim Sharabayko --- srtcore/api.h | 3 +- srtcore/buffer_rcv.cpp | 823 ++++++++++++++++++++++++++++++--------- srtcore/buffer_rcv.h | 614 +++++++++++++++++++++++++---- srtcore/common.h | 12 +- srtcore/core.cpp | 193 +++++---- srtcore/core.h | 10 +- srtcore/utilities.h | 33 +- test/test_buffer_rcv.cpp | 76 +++- 8 files changed, 1420 insertions(+), 344 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index d80611727..7af4873a6 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -134,7 +134,8 @@ class CUDTSocket return m_iBusy; } - + // XXX Controversial as to whether it should be guarded by this lock. + // It is used in many places without the lock, and it is also atomic. SRT_ATTR_GUARDED_BY(m_ControlLock) sync::atomic m_Status; //< current socket state diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 59d534003..aef13d047 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,22 +75,6 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. - // The right edge is included because we expect iFirstNonreadPos to be - // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) - { - if (iFirstNonreadPos == iStartPos) - return true; - - const int iLastPos = (iStartPos + iMaxPosOff) % iSize; - const bool isOverrun = iLastPos < iStartPos; - - if (isOverrun) - return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; - - return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; - } } @@ -119,13 +103,15 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) - , m_iStartSeqNo(initSeqNo) + , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. , m_iStartPos(0) + , m_iEndOff(0) + , m_iDropOff(0) , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) - , m_numOutOfOrderPackets(0) - , m_iFirstReadableOutOfOrder(-1) + , m_numNonOrderPackets(0) + , m_iFirstNonOrderMsgPos(CPos_TRAP) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -142,85 +128,352 @@ CRcvBuffer::~CRcvBuffer() { if (!it->pUnit) continue; - + m_pUnitQueue->makeUnitFree(it->pUnit); it->pUnit = NULL; } } -int CRcvBuffer::insert(CUnit* unit) +void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) +{ + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source + << ") start=" << m_iStartPos + << " end=+" << m_iEndOff + << " drop=+" << m_iDropOff + << " max-off=+" << m_iMaxPosOff + << " seq[start]=%" << m_iStartSeqNo.val()); +} + +CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno); IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - if (offset < 0) + if (offset < COff(0)) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); - return -2; + return InsertInfo(InsertInfo::BELATED); } + IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); - if (offset >= (int)capacity()) + if (offset >= COff(capacity())) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - return -3; + + InsertInfo ireport (InsertInfo::DISCREPANCY); + getAvailInfo((ireport)); + + IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); + + return ireport; } // 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; + const CPos newpktpos = incPos(m_iStartPos, offset); + const COff prev_max_off = m_iMaxPosOff; + bool extended_end = false; if (offset >= m_iMaxPosOff) - m_iMaxPosOff = offset + 1; + { + m_iMaxPosOff = offset + COff(1); + extended_end = true; + } // Packet already exists - SRT_ASSERT(pos >= 0 && pos < int(m_szSize)); - if (m_entries[pos].status != EntryState_Empty) + // (NOTE: the above extension of m_iMaxPosOff is + // possible even before checking that the packet + // exists because existence of a packet beyond + // the current max position is not possible). + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); - return -1; + IF_HEAVY_LOGGING(debugShowState((debug_source + " redundant").c_str())); + return InsertInfo(InsertInfo::REDUNDANT); } - SRT_ASSERT(m_entries[pos].pUnit == NULL); + SRT_ASSERT(m_entries[newpktpos].pUnit == NULL); m_pUnitQueue->makeUnitTaken(unit); - m_entries[pos].pUnit = unit; - m_entries[pos].status = EntryState_Avail; + m_entries[newpktpos].pUnit = unit; + m_entries[newpktpos].status = EntryState_Avail; countBytes(1, (int)unit->m_Packet.getLength()); + // Set to a value, if due to insertion there was added + // a packet that is earlier to be retrieved than the earliest + // currently available packet. + time_point earlier_time = updatePosInfo(unit, prev_max_off, offset, extended_end); + + InsertInfo ireport (InsertInfo::INSERTED); + ireport.first_time = earlier_time; + // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numOutOfOrderPackets; - onInsertNotInOrderPacket(pos); + ++m_numNonOrderPackets; + onInsertNonOrderPacket(newpktpos); } updateNonreadPos(); + + // This updates only the first_seq and avail_range fields. + getAvailInfo((ireport)); + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); - return 0; + IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); + + return ireport; } +void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) +{ + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position, if it's set + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + const CPacket* pkt = &packetAt(m_iStartPos); + SRT_ASSERT(pkt); + w_if.avail_range = m_iEndOff; + w_if.first_seq = CSeqNo(pkt->getSeqNo()); + return; + } + + // If not the first position, probe the skipped positions: + // - for live mode, check the DROP position + // (for potential after-drop reading) + // - for message mode, check the non-order message position + // (for potential out-of-oder message delivery) + + const CPacket* pkt = NULL; + if (m_tsbpd.isEnabled()) + { + // With TSBPD you can rely on drop position, if set + // Drop position must point always to a valid packet. + // Drop position must start from +1; 0 means no drop. + if (m_iDropOff) + { + pkt = &packetAt(incPos(m_iStartPos, m_iDropOff)); + SRT_ASSERT(pkt); + } + } + else + { + // Message-mode: try non-order read position. + if (m_iFirstNonOrderMsgPos != CPos_TRAP) + { + pkt = &packetAt(m_iFirstNonOrderMsgPos); + SRT_ASSERT(pkt); + } + } + + if (!pkt) + { + // This is default, but set just in case + // The default seq is SRT_SEQNO_NONE. + w_if.avail_range = COff(0); + return; + } + + // TODO: we know that at least 1 packet is available, but only + // with m_iEndOff we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_if.avail_range = COff(1); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); +} + + +// This function is called exclusively after packet insertion. +// This will update also m_iEndOff and m_iDropOff fields (the latter +// regardless of the TSBPD mode). +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, + const COff offset, + const bool extended_end) +{ + time_point earlier_time; + + // Update flags + // Case [A]: insertion of the packet has extended the busy region. + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndOff == prev_max_off) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndOff now shifts by 1, + // and m_iDropOff is set to 0 as there's no gap. + m_iEndOff = m_iMaxPosOff; + m_iDropOff = 0; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndOff STAYS WHERE IT IS. + m_iDropOff = m_iMaxPosOff - 1; + } + } + } + // + // Since this place, every 'offset' is in the range + // between m_iEndOff (inclusive) and m_iMaxPosOff. + else if (offset == m_iEndOff) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndOff == 0) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(); + } + else if (offset < m_iDropOff) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropOff, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropOff = offset; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iEndOff == 0) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [B] in which nothing is to be updated. + + return earlier_time; +} + +// This function is called when the m_iEndOff has been set to a new +// position and the m_iDropOff should be calculated since that position again. +void CRcvBuffer::updateGapInfo() +{ + COff from = m_iEndOff; + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); + + CPos pos = incPos(m_iStartPos, from); + + if (m_entries[pos].status == EntryState_Avail) + { + CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); + + for (; pos != end_pos; pos = incPos(pos)) + { + if (m_entries[pos].status != EntryState_Avail) + break; + } + + m_iEndOff = offPos(m_iStartPos, pos); + } + + // XXX This should be this way, but there are still inconsistencies + // in the message code. + //USE: SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); + + // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP + // is set. This option is not handled in message mode, only in live mode. + // Dropping by packet makes sense only in case of packetwise reading, + // which isn't the case of neither stream nor message mode. + if (!m_tsbpd.isEnabled()) + { + m_iDropOff = 0; + return; + } + + // Do not touch m_iDropOff if it's still beside the contiguous + // region. DO NOT SEARCH for m_iDropOff if m_iEndOff is max + // because this means that the whole buffer is contiguous. + // That would simply find nothing and only uselessly burden the + // performance by searching for a not present empty cell. + + // Also check if the current drop position is a readable packet. + // If not, start over. + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + + if (m_iDropOff < m_iEndOff || m_entries[drop_pos].status != EntryState_Avail) + { + m_iDropOff = 0; + if (m_iEndOff < m_iMaxPosOff) + { + CPos start = incPos(m_iStartPos, m_iEndOff + 1), + end = incPos(m_iStartPos, m_iEndOff); + + for (CPos i = start; i != end; i = incPos(i)) + { + if (m_entries[i].status == EntryState_Avail) + { + m_iDropOff = offPos(m_iStartPos, i); + break; + } + } + + // Must be found somewhere, worst case at the position + // of m_iMaxPosOff-1. If no finding loop caught it somehow, + // it will remain at 0. The case when you have empty packets + // in the busy range is only with message mode after reading + // packets out-of-order, but this doesn't use tsbpd mode. + SRT_ASSERT(m_iDropOff != 0); + } + } +} + +/// Request to remove from the receiver buffer +/// all packets with earlier sequence than @a seqno. +/// (Meaning, the packet with given sequence shall +/// be the first packet in the buffer after the operation). std::pair CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); - int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); return std::make_pair(0, 0); } - m_iMaxPosOff -= len; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + m_iMaxPosOff = decOff(m_iMaxPosOff, len); + m_iEndOff = decOff(m_iEndOff, len); + m_iDropOff = decOff(m_iDropOff, len); int iNumDropped = 0; // Number of dropped packets that were missing. int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. @@ -229,9 +482,9 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. if (m_entries[m_iStartPos].status == EntryState_Avail) - ++iNumDiscarded; + ++iNumDiscarded; else if (m_entries[m_iStartPos].status == EntryState_Empty) - ++iNumDropped; + ++iNumDropped; dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); @@ -240,19 +493,23 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) } // Update positions - m_iStartSeqNo = seqno; + m_iStartSeqNo = CSeqNo(seqno); // Move forward if there are "read/drop" entries. + // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); + updateGapInfo(); + // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange(m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); } if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableOutOfOrder(); + updateFirstReadableNonOrder(); + IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return std::make_pair(iNumDropped, iNumDiscarded); } @@ -261,7 +518,7 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + const int32_t end_seqno = CSeqNo::incseq(m_iStartSeqNo.val(), m_iMaxPosOff); const std::pair numDropped = dropUpTo(end_seqno); return numDropped.first + numDropped.second; } @@ -278,24 +535,24 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro } // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; + const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo.val() << "."); return 0; } const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING); - int minDroppedOffset = -1; + COff minDroppedOffset (-1); int iDropCnt = 0; - const int start_off = max(0, offset_a); - const int start_pos = incPos(m_iStartPos, start_off); - const int end_off = min((int) m_szSize - 1, offset_b + 1); - const int end_pos = incPos(m_iStartPos, end_off); + const COff start_off = COff(max(0, offset_a)); + const CPos start_pos = incPos(m_iStartPos, start_off); + const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); + const CPos end_pos = incPos(m_iStartPos, end_off); bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (int i = start_pos; i != end_pos; i = incPos(i)) + for (CPos i = start_pos; i != end_pos; i = incPos(i)) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -310,7 +567,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (bKeepExisting && bnd == PB_SOLO) { bDropByMsgNo = false; // Solo packet, don't search for the rest of the message. - LOGC(rbuflog.Debug, + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %" << packetAt(i).getSeqNo() << "."); continue; @@ -336,6 +593,14 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro minDroppedOffset = offPos(m_iStartPos, i); } + if (end_off > m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo.val(), end_off) + << " - updating the busy region"); + m_iMaxPosOff = end_off; + } + if (bDropByMsgNo) { // If msgno is specified, potentially not the whole message was dropped using seqno range. @@ -343,8 +608,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - const int stop_pos = decPos(m_iStartPos); - for (int i = start_pos; i != stop_pos; i = decPos(i)) + const CPos stop_pos = decPos(m_iStartPos); + for (CPos i = start_pos; i != stop_pos; i = decPos(i)) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -353,7 +618,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro const PacketBoundary bnd = packetAt(i).getMsgBoundary(); const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); if (msgseq != msgno) - break; + break; if (bKeepExisting && bnd == PB_SOLO) { @@ -376,9 +641,26 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); } + if (iDropCnt) + { + // We don't need the drop position, if we allow to drop messages by number + // and with that value we risk that drop was pointing to a dropped packet. + // Theoretically to make it consistent we need to shift the value to the + // next found packet, but we don't need this information if we use the message + // mode (because drop-by-packet is not supported in this mode) and this + // will burden the performance for nothing. + m_iDropOff = 0; + } + // Check if units before m_iFirstNonreadPos are dropped. const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); + + updateGapInfo(); + + IF_HEAVY_LOGGING(debugShowState( + ("dropmsg off %" + Sprint(seqnolo) + " #" + Sprint(msgno)).c_str())); + if (needUpdateNonreadPos) { m_iFirstNonreadPos = m_iStartPos; @@ -386,24 +668,46 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableNonOrder()) + m_iFirstNonOrderMsgPos = CPos_TRAP; + updateFirstReadableNonOrder(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } -int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const +{ + if (m_iEndOff == 0) + { + // Initial contiguous region empty (including empty buffer). + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo.val()); + w_seq = m_iStartSeqNo.val(); + return m_iMaxPosOff > 0; + } + + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff); + + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff + << " maxD=" << m_iMaxPosOff + << " base=%" << m_iStartSeqNo.val() + << " end=%" << w_seq); + + return (m_iEndOff < m_iMaxPosOff); +} + +int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos == CPos_TRAP) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + const CPos readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; + const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); @@ -412,8 +716,21 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) char* dst = data; int pkts_read = 0; int bytes_extracted = 0; // The total number of bytes extracted from the buffer. - const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed - for (int i = readPos;; i = incPos(i)) + + int32_t out_seqlo = SRT_SEQNO_NONE; + int32_t out_seqhi = SRT_SEQNO_NONE; + + // As we have a green light for reading, it is already known that + // we're going to either remove or extract packets from the buffer, + // so drop position won't count anymore. + // + // The END position should be updated, that is: + // - remain just updated by the shifted start position if it's still ahead + // - recalculated from 0 again otherwise + m_iDropOff = 0; + int nskipped = 0; + + for (CPos i = readPos;; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); if (!m_entries[i].pUnit) @@ -426,6 +743,11 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); + if (out_seqlo == SRT_SEQNO_NONE) + out_seqlo = pktseqno; + + out_seqhi = pktseqno; + // unitsize can be zero const size_t unitsize = std::min(remain, pktsize); memcpy(dst, packet.m_pcData, unitsize); @@ -438,8 +760,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (m_tsbpd.isEnabled()) updateTsbPdTimeBase(packet.getMsgTimeStamp()); - if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) - --m_numOutOfOrderPackets; + if (m_numNonOrderPackets && !packet.getMsgOrderFlag()) + --m_numNonOrderPackets; const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) @@ -454,12 +776,11 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) msgctrl->pktseq = pktseqno; releaseUnitInPos(i); - if (updateStartPos) + if (isReadingFromStart) { m_iStartPos = incPos(i); - --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff >= 0); - m_iStartSeqNo = CSeqNo::incseq(pktseqno); + m_iStartSeqNo = CSeqNo(pktseqno) + 1; + ++nskipped; } else { @@ -469,26 +790,44 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (pbLast) { - if (readPos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + if (readPos == m_iFirstNonOrderMsgPos) + { + m_iFirstNonOrderMsgPos = CPos_TRAP; + m_iDropOff = 0; // always set to 0 in this mode. + } break; } } + if (nskipped) + { + // This means that m_iStartPos HAS BEEN shifted by that many packets. + // Update offset variables + m_iMaxPosOff -= nskipped; + + // This is checked as the PB_LAST flag marked packet should still + // be extracted in the existing period. + SRT_ASSERT(m_iMaxPosOff >= 0); + + m_iEndOff = decOff(m_iEndOff, len); + } countBytes(-pkts_read, -bytes_extracted); releaseNextFillerEntries(); - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + // This will update the end position + updateGapInfo(); + + if (!isInUsedRange(m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } if (!m_tsbpd.isEnabled()) - // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // We need updateFirstReadableNonOrder() here even if we are reading inorder, // incase readable inorder packets are all read out. - updateFirstReadableOutOfOrder(); + updateFirstReadableNonOrder(); const int bytes_read = int(dst - data); if (bytes_read < bytes_extracted) @@ -498,6 +837,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + if (pw_seqrange) + *pw_seqrange = make_pair(out_seqlo, out_seqhi); + + IF_HEAVY_LOGGING(debugShowState("readmsg")); return bytes_read; } @@ -531,8 +874,8 @@ namespace { int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) { - int p = m_iStartPos; - const int end_pos = m_iFirstNonreadPos; + CPos p = m_iStartPos; + const CPos end_pos = m_iFirstNonreadPos; const bool bTsbPdEnabled = m_tsbpd.isEnabled(); const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); @@ -542,7 +885,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) { if (!m_entries[p].pUnit) { - p = incPos(p); + // REDUNDANT? p = incPos(p); // Return abandons the loop anyway. LOGC(rbuflog.Error, log << "readBufferTo: IPE: NULL unit found in file transmission"); return -1; } @@ -558,7 +901,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) << " PKT TS=" << FormatTime(tsPlay)); if ((tsPlay > now)) - break; /* too early for this unit, return whatever was copied */ + break; // too early for this unit, return whatever was copied } const int pktlen = (int)pkt.getLength(); @@ -577,7 +920,10 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; SRT_ASSERT(m_iMaxPosOff >= 0); - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + m_iEndOff = decOff(m_iEndOff, 1); + m_iDropOff = decOff(m_iDropOff, 1); + + m_iStartSeqNo = m_iStartSeqNo.inc(); } else m_iNotch += rs; @@ -592,16 +938,18 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } + IF_HEAVY_LOGGING(debugShowState("readbuf")); return iBytesRead; } @@ -617,15 +965,12 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } int CRcvBuffer::getRcvDataSize() const { - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - - return int(m_szSize + m_iFirstNonreadPos - m_iStartPos); + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -636,7 +981,7 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + CPos lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -646,11 +991,11 @@ int CRcvBuffer::getTimespan_ms() const { lastpos = decPos(lastpos); } - + if (m_entries[lastpos].pUnit == NULL) return 0; - int startpos = m_iStartPos; + CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { startpos = incPos(startpos); @@ -680,33 +1025,42 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); - for (int i = m_iStartPos; i != end_pos; i = incPos(i)) - { - // TODO: Maybe check status? - if (!m_entries[i].pUnit) - continue; + // Default: no packet available. + PacketInfo pi = { SRT_SEQNO_NONE, false, time_point() }; - const CPacket& packet = packetAt(i); - const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; - return info; + const CPacket* pkt = NULL; + + // Very first packet available with no gap. + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + SRT_ASSERT(m_entries[m_iStartPos].pUnit); + pkt = &packetAt(m_iStartPos); + } + // If not, get the information from the drop + else if (m_iDropOff) + { + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + SRT_ASSERT(m_entries[drop_pos].pUnit); + pkt = &packetAt(drop_pos); + pi.seq_gap = true; // Available, but after a drop. + } + else + { + // If none of them point to a valid packet, + // there is no packet available; + return pi; } - const PacketInfo info = { -1, false, time_point() }; - return info; + pi.seqno = pkt->getSeqNo(); + pi.tsbpd_time = getPktTsbPdTime(pkt->getMsgTimeStamp()); + return pi; } std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable()); - return std::pair(m_iStartSeqNo, seqno_last); -} - -size_t CRcvBuffer::countReadable() const -{ - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - return m_szSize + m_iFirstNonreadPos - m_iStartPos; + const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); + const CSeqNo seqno_last = m_iStartSeqNo + nonread_off; + return std::pair(m_iStartSeqNo.val(), seqno_last.val()); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -717,8 +1071,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } if (!haveInorderPackets) @@ -742,11 +1096,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstReadableOutOfOrder >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstNonOrderMsgPos != CPos_TRAP) { - SRT_ASSERT(m_numOutOfOrderPackets > 0); - const CPacket& packet = packetAt(m_iFirstReadableOutOfOrder); + SRT_ASSERT(m_numNonOrderPackets > 0); + const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -767,7 +1121,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no int32_t CRcvBuffer::getFirstNonreadSeqNo() const { const int offset = offPos(m_iStartPos, m_iFirstNonreadPos); - return CSeqNo::incseq(m_iStartSeqNo, offset); + return m_iStartSeqNo.inc(offset).val(); } void CRcvBuffer::countBytes(int pkts, int bytes) @@ -784,7 +1138,7 @@ void CRcvBuffer::countBytes(int pkts, int bytes) } } -void CRcvBuffer::releaseUnitInPos(int pos) +void CRcvBuffer::releaseUnitInPos(CPos pos) { CUnit* tmp = m_entries[pos].pUnit; m_entries[pos] = Entry(); // pUnit = NULL; status = Empty @@ -792,7 +1146,7 @@ void CRcvBuffer::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } -bool CRcvBuffer::dropUnitInPos(int pos) +bool CRcvBuffer::dropUnitInPos(CPos pos) { if (!m_entries[pos].pUnit) return false; @@ -802,27 +1156,49 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { - --m_numOutOfOrderPackets; - if (pos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + --m_numNonOrderPackets; + if (pos == m_iFirstNonOrderMsgPos) + m_iFirstNonOrderMsgPos = CPos_TRAP; } releaseUnitInPos(pos); return true; } -void CRcvBuffer::releaseNextFillerEntries() +int CRcvBuffer::releaseNextFillerEntries() { - int pos = m_iStartPos; + CPos pos = m_iStartPos; + int nskipped = 0; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + if (nskipped == m_iMaxPosOff) + { + // This should never happen. All the previously read- or drop-marked + // packets should be contained in the range up to m_iMaxPosOff. Do not + // let the buffer ride any further and report the problem. Still stay there. + LOGC(rbuflog.Error, log << "releaseNextFillerEntries: IPE: Read/Drop status outside the busy range!"); + break; + } + + m_iStartSeqNo = m_iStartSeqNo.inc(); releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; - --m_iMaxPosOff; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + ++nskipped; + } + + if (!nskipped) + { + return nskipped; } + + m_iMaxPosOff -= nskipped; + m_iEndOff = decOff(m_iEndOff, nskipped); + + // Drop off will be updated after that call, if needed. + m_iDropOff = 0; + + return nskipped; } // TODO: Is this function complete? There are some comments left inside. @@ -831,21 +1207,25 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + const CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. - int pos = m_iFirstNonreadPos; + CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (int i = pos; i != end_pos; i = incPos(i)) + for (CPos i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { break; } + // m_iFirstNonreadPos is moved to the first position BEHIND + // the PB_LAST packet of the message. There's no guaratnee that + // the cell at this position isn't empty. + // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { @@ -861,9 +1241,9 @@ void CRcvBuffer::updateNonreadPos() } } -int CRcvBuffer::findLastMessagePkt() +CPos CRcvBuffer::findLastMessagePkt() { - for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -873,12 +1253,12 @@ int CRcvBuffer::findLastMessagePkt() } } - return -1; + return CPos_TRAP; } -void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(CPos insertPos) { - if (m_numOutOfOrderPackets == 0) + if (m_numNonOrderPackets == 0) return; // If the following condition is true, there is already a packet, @@ -887,7 +1267,7 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstReadableOutOfOrder >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) return; // Just a sanity check. This function is called when a new packet is added. @@ -900,34 +1280,34 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstReadableOutOfOrder = insertPos; + // m_iFirstNonOrderMsgPos = insertPos; // return; //} const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (scanNonOrderMessageRight(insertPos, msgNo) != CPos_TRAP); if (!hasLast) return; - const int firstPktPos = (boundary & PB_FIRST) + const CPos firstPktPos = (boundary & PB_FIRST) ? insertPos - : scanNotInOrderMessageLeft(insertPos, msgNo); - if (firstPktPos < 0) + : scanNonOrderMessageLeft(insertPos, msgNo); + if (firstPktPos == CPos_TRAP) return; - m_iFirstReadableOutOfOrder = firstPktPos; + m_iFirstNonOrderMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableOutOfOrder() +bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + const CPos endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) // ++pos) { if (!m_entries[pos].pUnit) return false; @@ -948,30 +1328,31 @@ bool CRcvBuffer::checkFirstReadableOutOfOrder() return false; } -void CRcvBuffer::updateFirstReadableOutOfOrder() +void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos != CPos_TRAP) return; if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numOutOfOrderPackets; + int outOfOrderPktsRemain = (int) m_numNonOrderPackets; // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - int posFirst = -1; - int posLast = -1; + CPos posFirst = CPos_TRAP; + CPos posLast = CPos_TRAP; int msgNo = -1; - for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) { if (!m_entries[pos].pUnit) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -979,7 +1360,8 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() if (pkt.getMsgOrderFlag()) // Skip in order packet { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -994,13 +1376,14 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } if (boundary & PB_LAST) { - m_iFirstReadableOutOfOrder = posFirst; + m_iFirstNonOrderMsgPos = posFirst; return; } @@ -1011,15 +1394,15 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() return; } -int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (startPos == lastPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { pos = incPos(pos); @@ -1031,7 +1414,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1039,30 +1422,30 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const return pos; } while (pos != lastPos); - return -1; + return CPos_TRAP; } -int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. if (startPos == m_iStartPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { pos = decPos(pos); if (!m_entries[pos].pUnit) - return -1; + return CPos_TRAP; const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1070,7 +1453,7 @@ int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const return pos; } while (pos != m_iStartPos); - return -1; + return CPos_TRAP; } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) @@ -1108,11 +1491,11 @@ void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp) m_tsbpd.updateBaseTime(usPktTimestamp); } -string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1124,7 +1507,7 @@ string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNo if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1173,4 +1556,90 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) m_mavg.update(now, pkts, bytes, timespan_ms); } +int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) +{ + // This means that there are no lost seqs at all, no matter + // from which position they would have to be checked. + if (m_iEndOff == m_iMaxPosOff) + return SRT_SEQNO_NONE; + + COff offset = COff(CSeqNo(fromseq) - m_iStartSeqNo); + + // Check if it's still inside the buffer. + // Skip the region from 0 to m_iEndOff because this + // region is by definition contiguous and contains no loss. + if (offset < m_iEndOff || offset >= m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + return SRT_SEQNO_NONE; + } + + // Check if this offset is equal to m_iEndOff. If it is, + // then you have the loss sequence exactly the one that + // was passed. Skip now, pw_end was not requested. + if (offset == m_iEndOff) + { + if (pw_end) + { + // If the offset is exactly at m_iEndOff, then + // m_iDropOff will mark the end of gap. + if (m_iDropOff) + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), m_iDropOff); + else + { + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); + *pw_end = fromseq; + } + } + return fromseq; + } + + int ret_seq = SRT_SEQNO_NONE; + int loss_off = 0; + // Now find the first empty position since here, + // up to m_iMaxPosOff. Checking against m_iDropOff + // makes no sense because if it is not 0, you'll + // find it earlier by checking packet presence. + for (int off = offset; off < m_iMaxPosOff; ++off) + { + CPos ipos ((m_iStartPos + off) % m_szSize); + if (m_entries[ipos].status == EntryState_Empty) + { + ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); + loss_off = off; + break; + } + } + + if (ret_seq == SRT_SEQNO_NONE) + { + // This is theoretically possible if we search from behind m_iEndOff, + // after m_iDropOff. This simply means that we are trying to search + // behind the last gap in the buffer. + return ret_seq; + } + + // We get this position, so search for the end of gap + if (pw_end) + { + for (int off = loss_off+1; off < m_iMaxPosOff; ++off) + { + CPos ipos ((m_iStartPos + off) % m_szSize); + if (m_entries[ipos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); + return ret_seq; + } + } + + // Should not be possible to not find an existing packet + // following the gap, otherwise there would be no gap. + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: gap since %" << ret_seq << " not covered by existing packet"); + *pw_end = ret_seq; + } + return ret_seq; +} + + } // namespace srt diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index c5fca428b..80fe1d1ec 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -15,32 +15,390 @@ #include "common.h" #include "queue.h" #include "tsbpd_time.h" +#include "utilities.h" + +#define USE_WRAPPERS 0 +#define USE_OPERATORS 0 namespace srt { -/* - * Circular receiver buffer. - * - * |<------------------- m_szSize ---------------------------->| - * | |<------------ m_iMaxPosOff ----------->| | - * | | | | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | | - * | \__last pkt received - * | - * \___ m_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_: - */ +// DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) + +// NOTE: This below series of definitions for CPos and COff +// are here for development support only, but they are not in +// use in the release code - there CPos and COff are aliases to int. +#if USE_WRAPPERS +struct CPos +{ + int value; +#if USE_OPERATORS + const size_t* psize; + int isize() const {return *psize;} +#endif + +#if USE_OPERATORS + explicit CPos(const size_t* ps SRT_ATR_UNUSED, int val) + : value(val) + , psize(ps) + {} + +#else + explicit CPos(int val): value(val) {} +#endif + + int val() const { return value; } + explicit operator int() const {return value;} + + CPos(const CPos& src): value(src.value) +#if USE_OPERATORS + , psize(src.psize) +#endif + {} + CPos& operator=(const CPos& src) + { +#if USE_OPERATORS + psize = src.psize; +#endif + value = src.value; + return *this; + } + +#if USE_OPERATORS + int cmp(CPos other, CPos start) const + { + int pos2 = value; + int pos1 = other.value; + + const int off1 = pos1 >= start.value ? pos1 - start.value : pos1 + start.isize() - start.value; + const int off2 = pos2 >= start.value ? pos2 - start.value : pos2 + start.isize() - start.value; + + return off2 - off1; + } + + CPos& operator--() + { + if (value == 0) + value = isize() - 1; + else + --value; + return *this; + } + + CPos& operator++() + { + ++value; + if (value == isize()) + value = 0; + return *this; + } +#endif + + bool operator == (CPos other) const { return value == other.value; } + bool operator != (CPos other) const { return value != other.value; } +}; + +struct COff +{ + int value; + explicit COff(int v): value(v) {} + COff& operator=(int v) { value = v; return *this; } + + int val() const { return value; } + explicit operator int() const {return value;} + + COff& operator--() { --value; return *this; } + COff& operator++() { ++value; return *this; } + + COff operator--(int) { int v = value; --value; return COff(v); } + COff operator++(int) { int v = value; ++value; return COff(v); } + + COff operator+(COff other) const { return COff(value + other.value); } + COff operator-(COff other) const { return COff(value - other.value); } + COff& operator+=(COff other) { value += other.value; return *this;} + COff& operator-=(COff other) { value -= other.value; return *this;} + + bool operator == (COff other) const { return value == other.value; } + bool operator != (COff other) const { return value != other.value; } + bool operator < (COff other) const { return value < other.value; } + bool operator > (COff other) const { return value > other.value; } + bool operator <= (COff other) const { return value <= other.value; } + bool operator >= (COff other) const { return value >= other.value; } + + // Exceptionally allow modifications of COff by a bare integer + COff operator+(int other) const { return COff(value + other); } + COff operator-(int other) const { return COff(value - other); } + COff& operator+=(int other) { value += other; return *this;} + COff& operator-=(int other) { value -= other; return *this;} + + bool operator == (int other) const { return value == other; } + bool operator != (int other) const { return value != other; } + bool operator < (int other) const { return value < other; } + bool operator > (int other) const { return value > other; } + bool operator <= (int other) const { return value <= other; } + bool operator >= (int other) const { return value >= other; } + + friend bool operator == (int value, COff that) { return value == that.value; } + friend bool operator != (int value, COff that) { return value != that.value; } + friend bool operator < (int value, COff that) { return value < that.value; } + friend bool operator > (int value, COff that) { return value > that.value; } + friend bool operator <= (int value, COff that) { return value <= that.value; } + friend bool operator >= (int value, COff that) { return value >= that.value; } + + operator bool() const { return value != 0; } +}; + +#if USE_OPERATORS + +inline CPos operator+(const CPos& pos, COff off) +{ + int val = pos.value + off.value; + while (val >= pos.isize()) + val -= pos.isize(); + return CPos(pos.psize, val); +} + +inline CPos operator-(const CPos& pos, COff off) +{ + int val = pos.value - off.value; + while (val < 0) + val += pos.isize(); + return CPos(pos.psize, val); +} + +// Should verify that CPos use the same size! +inline COff operator-(CPos later, CPos earlier) +{ + if (later.value < earlier.value) + return COff(later.value + later.isize() - earlier.value); + + return COff(later.value - earlier.value); +} + +inline CSeqNo operator+(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::incseq(seq.val(), off.val()); + return CSeqNo(val); +} + +inline CSeqNo operator-(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::decseq(seq.val(), off.val()); + return CSeqNo(val); +} + + +#endif +const CPos CPos_TRAP (-1); + +#else +typedef int CPos; +typedef int COff; +const int CPos_TRAP = -1; +#endif + +// +// Circular receiver buffer. +// +// ICR = Initial Contiguous Region: all cells here contain valid packets +// SCRAP REGION: Region with possibly filled or empty cells +// NOTE: in scrap region, the first cell is empty and the last one filled. +// SPARE REGION: Region without packets +// +// | BUSY REGION | +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | /FIRST-GAP | | +// |<------------------- m_szSize ---------------------------->| +// | |<------------ m_iMaxPosOff ----------->| | +// | | | | | | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | | | | +// | | | \__last pkt received +// |<------------->| m_iDropOff | +// | | | +// |<--------->| m_iEndOff | +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status: +// EntryState_Empty: No packet was ever received here +// EntryState_Avail: The packet is ready for reading +// EntryState_Read: The packet is non-order-read +// EntryState_Drop: The packet was requested to drop +// +// thread safety: +// m_iStartPos: CUDT::m_RecvLock +// first_unack_pos_: CUDT::m_AckLock +// m_iMaxPosOff: none? (modified on add and ack +// m_iFirstNonreadPos: +// +// +// m_iStartPos: the first packet that should be read (might be empty) +// m_iEndOff: shift to the end of contiguous range. This points always to an empty cell. +// m_iDropPos: shift a packet available for retrieval after a drop. If 0, no such packet. +// +// Operational rules: +// +// Initially: +// m_iStartPos = 0 +// m_iEndOff = 0 +// m_iDropOff = 0 +// +// When a packet has arrived, then depending on where it landed: +// +// 1. Position: next to the last read one and newest +// +// m_iStartPos unchanged. +// m_iEndOff shifted by 1 +// m_iDropOff = 0 +// +// 2. Position: after a loss, newest. +// +// m_iStartPos unchanged. +// m_iEndOff unchanged. +// m_iDropOff: +// - set to this packet, if m_iDropOff == 0 or m_iDropOff is past this packet +// - otherwise unchanged +// +// 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if m_iDropPos == m_iEndPos, set to this +// - if m_iDropPos %> this sequence, set to this +// - otherwise unchanged +// +// 4. Position: after a loss, sealing -- seq equal to position of m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos: +// - since this position, search the first free cell +// - if reached the end of filled region (m_iMaxPosOff), stay there. +// m_iDropPos: +// - start from the value equal to m_iEndPos +// - walk at maximum to m_iMaxPosOff +// - find the first existing packet +// NOTE: +// If there are no "after gap" packets, then m_iMaxPosOff == m_iEndPos. +// If there is one existing packet, then one loss, then one packet, it +// should be that m_iEndPos = m_iStartPos %+ 1, m_iDropPos can reach +// to m_iStartPos %+ 2 position, and m_iMaxPosOff == m_iStartPos %+ 3. +// +// To wrap up: +// +// Let's say we have the following possibilities in a general scheme: +// +// +// [D] [C] [B] [A] (insertion cases) +// | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | +// +// See the CRcvBuffer::updatePosInfo method for detailed implementation. +// +// WHEN INSERTING A NEW PACKET: +// +// If the incoming sequence maps to newpktpos that is: +// +// * newpktpos <% (start) : discard the packet and exit +// * newpktpos %> (size) : report discrepancy, discard and exit +// * newpktpos %> (start) and: +// * EXISTS: discard and exit (NOTE: could be also < (end)) +// [A]* seq == m_iMaxPosOff +// --> INC m_iMaxPosOff +// * m_iEndPos == previous m_iMaxPosOff +// * previous m_iMaxPosOff + 1 == m_iMaxPosOff +// --> m_iEndPos = m_iMaxPosOff +// --> m_iDropPos = m_iEndPos +// * otherwise (means the new packet caused a gap) +// --> m_iEndPos REMAINS UNCHANGED +// --> m_iDropPos = POSITION(m_iMaxPosOff) +// COMMENT: +// If this above condition isn't satisfied, then there are +// gaps, first at m_iEndPos, and m_iDropPos is at furthest +// equal to m_iMaxPosOff %- 1. The inserted packet is outside +// both the contiguous region and the following scratched region, +// so no updates on m_iEndPos and m_iDropPos are necessary. +// +// NOTE +// SINCE THIS PLACE seq cannot be a sequence of an existing packet, +// which means that earliest newpktpos == m_iEndPos, up to == m_iMaxPosOff -% 2. +// +// * otherwise (newpktpos <% max-pos): +// [D]* newpktpos == m_iEndPos: +// --> (search FIRST GAP and FIRST AFTER-GAP) +// --> m_iEndPos: increase until reaching m_iMaxPosOff +// * m_iEndPos <% m_iMaxPosOff: +// --> m_iDropPos = first VALID packet since m_iEndPos +% 1 +// * otherwise: +// --> m_iDropPos = m_iEndPos +// [B]* newpktpos %> m_iDropPos +// --> store, but do not update anything +// [C]* otherwise (newpktpos %> m_iEndPos && newpktpos <% m_iDropPos) +// --> store +// --> set m_iDropPos = newpktpos +// COMMENT: +// It is guaratneed that between m_iEndPos and m_iDropPos +// there is only a gap (series of empty cells). So wherever +// this packet lands, if it's next to m_iEndPos and before m_iDropPos +// it will be the only packet that violates the gap, hence this +// can be the only drop pos preceding the previous m_iDropPos. +// +// -- information returned to the caller should contain: +// 1. Whether adding to the buffer was successful. +// 2. Whether the "freshest" retrievable packet has been changed, that is: +// * in live mode, a newly added packet has earlier delivery time than one before +// * in stream mode, the newly added packet was at cell[0] +// * in message mode, if the newly added packet has: +// * completed the very first message +// * completed any message further than first that has out-of-order flag +// +// The information about a changed packet is important for the caller in +// live mode in order to notify the TSBPD thread. +// +// +// +// WHEN CHECKING A PACKET +// +// 1. Check the position at m_iStartPos. If there is a packet, +// return info at its position. +// +// 2. If position on m_iStartPos is empty, get the value of m_iDropPos. +// +// NOTE THAT: +// * if the buffer is empty, m_iDropPos == m_iStartPos and == m_iEndPos; +// note that m_iDropPos == m_iStartPos suffices to check that +// * if there is a packet in the buffer, but the first cell is empty, +// then m_iDropPos points to this packet, while m_iEndPos == m_iStartPos. +// Check then m_iStartPos == m_iEndPos to recognize it, and if then +// m_iDropPos isn't equal to them, you can read with dropping. +// * If cell[0] is valid, there could be only at worst cell[1] empty +// and cell[2] pointed by m_iDropPos. +// +// 3. In case of time-based checking for live mode, return empty packet info, +// if this packet's time is later than given time. +// +// WHEN EXTRACTING A PACKET +// +// 1. Extraction is only possible if there is a packet at cell[0]. +// 2. If there's no packet at cell[0], the application may request to +// drop up to the given packet, or drop the whole message up to +// the beginning of the next message. +// 3. In message mode, extraction can only extract a full message, so +// if there's no full message ready, nothing is extracted. +// 4. When the extraction region is defined, the m_iStartPos is shifted +// by the number of extracted packets. +// 5. If m_iEndPos <% m_iStartPos (after update), m_iEndPos should be +// set by searching from m_iStartPos up to m_iMaxPosOff for an empty cell. +// 6. m_iDropPos must be always updated. If m_iEndPos == m_iMaxPosOff, +// m_iDropPos is set to their value. Otherwise start from m_iEndPos +// and search a valid packet up to m_iMaxPosOff. +// 7. NOTE: m_iMaxPosOff is a delta, hence it must be set anew after update +// for m_iStartPos. +// class CRcvBuffer { @@ -53,16 +411,70 @@ class CRcvBuffer ~CRcvBuffer(); public: - /// Insert a unit into the buffer. - /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + + void debugShowState(const char* source); + + struct InsertInfo + { + enum Result { INSERTED = 0, REDUNDANT = -1, BELATED = -2, DISCREPANCY = -3 } result; + + // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. + + CSeqNo first_seq; // sequence of the first available readable packet + time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. + COff avail_range; + + InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, + time_point fp_time = time_point()) + : result(r), first_seq(fp_seq), first_time(fp_time), avail_range(range) + { + } + + InsertInfo() + : result(REDUNDANT), first_seq(SRT_SEQNO_NONE), avail_range(0) + { + } + + }; + + /// Inserts the unit with the data packet into the receiver buffer. + /// The result inform about the situation with the packet attempted + /// to be inserted and the readability of the buffer. /// - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. + /// @param [PASS] unit The unit that should be placed in the buffer /// - /// @return 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); + /// @return The InsertInfo structure where: + /// * result: the result of insertion, which is: + /// * INSERTED: successfully placed in the buffer + /// * REDUNDANT: not placed, the packet is already there + /// * BELATED: not placed, its sequence is in the past + /// * DISCREPANCY: not placed, the sequence is far future or OOTB + /// * first_seq: the earliest sequence number now avail for reading + /// * avail_range: how many packets are available for reading (1 if unknown) + /// * first_time: the play time of the earliest read-available packet + /// If there is no available packet for reading, first_seq == SRT_SEQNO_NONE. + /// + InsertInfo insert(CUnit* unit); + + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const COff offset, const bool extended_end); + void getAvailInfo(InsertInfo& w_if); + + /// Update the values of `m_iEndPos` and `m_iDropPos` in + /// case when `m_iEndPos` was updated to a position of a + /// nonempty cell. + /// + /// This function should be called after having m_iEndPos + /// has somehow be set to position of a non-empty cell. + /// This can happen by two reasons: + /// + /// - the cell has been filled by incoming packet + /// - the value has been reset due to shifted m_iStartPos + /// + /// This means that you have to search for a new gap and + /// update the m_iEndPos and m_iDropPos fields, or set them + /// both to the end of range if there are no loss gaps. + /// + void updateGapInfo(); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -98,16 +510,28 @@ class CRcvBuffer /// @return the number of packets actually dropped. int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting); + /// Extract the "expected next" packet sequence. + /// Extract the past-the-end sequence for the first packet + /// that is expected to arrive next with preserving the packet order. + /// If the buffer is empty or the very first cell is lacking a packet, + /// it returns the sequence assigned to the first cell. Otherwise it + /// returns the sequence representing the first empty cell (the next + /// cell to the last received packet, if there are no loss-holes). + /// @param [out] w_seq: returns the sequence (always valid) + /// @return true if this sequence is followed by any valid packets + bool getContiguousEnd(int32_t& w_seq) const; + /// Read the whole message from one or several packets. /// - /// @param [in,out] data buffer to write the message into. + /// @param [out] data buffer to write the message into. /// @param [in] len size of the buffer. - /// @param [in,out] message control data + /// @param [out,opt] message control data to be filled + /// @param [out,opt] pw_seqrange range of sequence numbers for packets belonging to the message /// /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. /// -1 on failure. - int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL, std::pair* pw_seqrange = NULL); /// Read acknowledged data into a user buffer. /// @param [in, out] dst pointer to the target user buffer. @@ -123,24 +547,25 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo; } + int32_t getStartSeqNo() const { return m_iStartSeqNo.val(); } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; } + void setStartSeqNo(int32_t seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. - size_t getAvailSize(int iFirstUnackSeqNo) const + size_t getAvailSize(int32_t iFirstUnackSeqNo) const { // Receiver buffer allows reading unacknowledged packets. // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const int iRBufSeqNo = getStartSeqNo(); + const int32_t iRBufSeqNo = m_iStartSeqNo.val(); if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + //if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); @@ -198,11 +623,11 @@ class CRcvBuffer /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; - size_t countReadable() const; + int32_t getFirstLossSeq(int32_t fromseq, int32_t* opt_end = NULL); bool empty() const { - return (m_iMaxPosOff == 0); + return (m_iMaxPosOff == COff(0)); } /// Returns the currently used number of cells, including @@ -255,54 +680,104 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: - inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } - inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } - inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + //* + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos + inc) % m_szSize); } + CPos decPos(CPos pos) const { return (pos - 1) >= 0 ? CPos(pos - 1) : CPos(m_szSize - 1); } + COff offPos(CPos pos1, CPos pos2) const + { + int diff = pos2 - pos1; + if (diff >= 0) + { + return COff(diff); + } + return COff(m_szSize + diff); + } + + COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + + static COff decOff(COff val, int shift) + { + int ival = val - shift; + if (ival < 0) + return COff(0); + return COff(ival); + } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. /// @param pos1 a position in the receiver buffer. /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. - inline int cmpPos(int pos2, int pos1) const + inline COff cmpPos(CPos pos2, CPos pos1) const { // XXX maybe not the best implementation, but this keeps up to the rule. // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. - const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; - const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; - return off2 - off1; + return posToOff(pos2) - posToOff(pos1); + } + // */ + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + static bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const CPos iLastPos = CPos((iStartPos + iMaxPosOff) % int(iSize)); + const bool isOverrun = iLastPos < iStartPos; + + if (isOverrun) + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + } + + bool isInUsedRange(CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == m_iStartPos) + return true; + + // DECODE the iFirstNonreadPos + int diff = iFirstNonreadPos - m_iStartPos; + if (diff < 0) + diff += m_szSize; + + return diff <= m_iMaxPosOff; } // NOTE: Assumes that pUnit != NULL - CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } - const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } + CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(CPos pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); void updateNonreadPos(); - void releaseUnitInPos(int pos); + void releaseUnitInPos(CPos pos); /// @brief Drop a unit from the buffer. /// @param pos position in the m_entries of the unit to drop. /// @return false if nothing to drop, true if the unit was dropped successfully. - bool dropUnitInPos(int pos); + bool dropUnitInPos(CPos pos); /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). - void releaseNextFillerEntries(); + /// + /// @return the range for which the start pos has been shifted + int releaseNextFillerEntries(); bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } /// Find position of the last packet of the message. - int findLastMessagePkt(); + CPos findLastMessagePkt(); /// Scan for availability of out of order packets. - void 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; + void onInsertNonOrderPacket(CPos insertpos); + // Check if m_iFirstNonOrderMsgPos is still readable. + bool checkFirstReadableNonOrder(); + void updateFirstReadableNonOrder(); + CPos scanNonOrderMessageRight(CPos startPos, int msgNo) const; + CPos scanNonOrderMessageLeft(CPos startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -358,15 +833,20 @@ class CRcvBuffer 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_iMaxPosOff; // the furthest data position - int m_iNotch; // the starting read point of the first unit + CSeqNo m_iStartSeqNo; + CPos m_iStartPos; // the head position for I/O (inclusive) + COff m_iEndOff; // past-the-end of the contiguous region since m_iStartOff + COff m_iDropOff; // points past m_iEndOff to the first deliverable after a gap, or == m_iEndOff if no such packet + CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + COff m_iMaxPosOff; // the furthest data position + int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) + + size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false - size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false - int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to - // read + /// Points to the first packet of a message that has out-of-order flag + /// and is complete (all packets from first to last are in the buffer). + /// If there is no such message in the buffer, it contains -1. + CPos m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -396,7 +876,7 @@ class CRcvBuffer /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. - std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + std::string strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const; private: CTsbpdTime m_tsbpd; diff --git a/srtcore/common.h b/srtcore/common.h index cda6f935b..92af89993 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -608,6 +608,8 @@ class CSeqNo explicit CSeqNo(int32_t v): value(v) {} + int32_t val() const { return value; } + // Comparison bool operator == (const CSeqNo& other) const { return other.value == value; } bool operator < (const CSeqNo& other) const @@ -711,26 +713,34 @@ class CSeqNo inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + SRT_ATR_NODISCARD CSeqNo inc() const { return CSeqNo(incseq(value)); } + inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + SRT_ATR_NODISCARD CSeqNo dec() const { return CSeqNo(decseq(value)); } + inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) + SRT_ATR_NODISCARD CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } + inline static int32_t decseq(int32_t seq, int32_t dec) { // Check if seq - dec < 0, but before it would have happened if ( seq < dec ) { int32_t left = dec - seq; // This is so many that is left after dragging dec to 0 - // So now decrement the (m_iMaxSeqNo+1) by "left" + // So now decrement the (m_iMaxSeqNo+1) by "left" return m_iMaxSeqNo - left + 1; } return seq - dec; } + SRT_ATR_NODISCARD CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } + static int32_t maxseq(int32_t seq1, int32_t seq2) { if (seqcmp(seq1, seq2) < 0) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 5b3eb2262..ebc881c2a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8119,12 +8119,19 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp m_tsLastSndTime.store(steady_clock::now()); } -// [[using locked(m_RcvBufferLock)]] bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) { + if (!m_pRcvBuffer) + { + LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); + return false; + } if (m_config.bTSBPD || !m_config.bMessageAPI) { - // The getFirstNonreadSeqNo() function retuens the sequence number of the first packet + // NOTE: it's not only about protecting the buffer itself, it's also protecting + // the section where the m_iRcvCurrSeqNo is updated. + ScopedLock buflock (m_RcvBufferLock); + // The getFirstNonreadSeqNo() function returns the sequence number of the first packet // that cannot be read. In cases when a message can consist of several data packets, // an existing packet of partially available message also cannot be read. // If TSBPD mode is enabled, a message must consist of a single data packet only. @@ -8142,28 +8149,17 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) return true; } - - { - ScopedLock losslock (m_RcvLossLock); - const int32_t seq = m_pRcvLossList->getFirstLostSeq(); - if (seq != SRT_SEQNO_NONE) - { - HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: first loss %" << seq << " (loss len=" << - m_pRcvLossList->getLossLength() << ")"); - w_seq = seq; - w_log_reason = "first lost"; - return true; - } - } - - w_seq = CSeqNo::incseq(m_iRcvCurrSeqNo); - HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: past-recv %" << w_seq); - w_log_reason = "expected next"; + + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; return true; } - int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { int nbsent = 0; @@ -8181,16 +8177,14 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) local_prevack = m_iDebugPrevLastAck; #endif - string reason; // just for "a reason" of giving particular % for ACK - // The TSBPD thread may change the first lost sequence record (TLPKTDROP). - // To avoid it the m_RcvBufferLock has to be acquired. - UniqueLock bufflock(m_RcvBufferLock); + // NOTE: the below calls do locking on m_RcvBufferLock. + // Hence up to the handling of lite ACK, the scoped lock is not applied. // The full ACK should be sent to indicate there is now available space in the RCV buffer // since the last full ACK. It should unblock the sender to proceed further. - const bool bNeedFullAck = (m_bBufferWasFull && getAvailRcvBufferSizeNoLock() > 0); + const bool bNeedFullAck = (m_bBufferWasFull && !isRcvBufferFull()); int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). - + string reason; // just for "a reason" of giving particular % for ACK if (!getFirstNoncontSequence((ack), (reason))) return nbsent; @@ -8204,7 +8198,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number if (size == SEND_LITE_ACK && !bNeedFullAck) { - bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); @@ -8212,6 +8205,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } + // Lock the group existence until this function ends. This will be useful + // also on other places. +#if ENABLE_BONDING + CUDTUnited::GroupKeeper gkeeper (uglobal(), m_parent); +#endif + + // There are new received packets to acknowledge, update related information. + /* tsbpd thread may also call ackData when skipping packet so protect code */ + UniqueLock bufflock(m_RcvBufferLock); + // IF ack %> m_iRcvLastAck // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) @@ -8278,23 +8281,22 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #endif - // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, - // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which - // will signal m_RecvDataCond when there's time to play for particular - // data packet. + // Signalling m_RecvDataCond is not done when TSBPD is on. + // This signalling is done in file mode in order to keep the + // API reader thread sleeping until there is a "bigger portion" + // of data to read. In TSBPD mode this isn't done because every + // packet has its individual delivery time and its readiness is signed + // off by the TSBPD thread. HLOGC(xtlog.Debug, log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); - if (m_bTsbPd) - { - /* Newly acknowledged data, signal TsbPD thread */ - CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdNeedsWakeup) - tslcc.notify_one(); - } - else + // There's no need to update TSBPD in the wake-on-recv state + // from ACK because it is being done already in the receiver thread + // when a newly inserted packet caused provision of a new candidate + // that could be delivered soon. Also, this flag is only used in TSBPD + // mode and can be only set to true in the TSBPD thread. + if (!m_bTsbPd) { { CUniqueSync rdcc (m_RecvLock, m_RecvDataCond); @@ -9213,14 +9215,27 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } } - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - rcvtscc.notify_one(); - } + + // NOTE: + // PREVIOUSLY done: notify on rcvtscc if m_bTsbPd + // OLD COMMENT: + // // When the drop request was received, it means that there are + // // packets for which there will never be ACK sent; if the TSBPD thread + // // is currently in the ACK-waiting state, it may never exit. + // + // Likely this is no longer necessary because: + // + // 1. If there's a play-ready packet, either in cell 0 or + // after a drop, TSBPD is sleeping timely, up to the play-time + // of the next ready packet (and the drop region concerned here + // is still a gap to be skipped for this). + // 2. TSBPD sleeps forever when the buffer is empty, in which case + // it will be always woken up on packet reception (see m_bTsbPdNeedsWakeup). + // And dropping won't happen in that case anyway. Note that the drop + // request will not drop packets that are already received. + // 3. TSBPD sleeps forever when the API call didn't extract the + // data that are ready to play. This isn't a problem if nothing + // except the API call would wake it up. } dropFromLossLists(dropdata[0], dropdata[1]); @@ -10200,7 +10215,7 @@ CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) SRT_ATR_UNUSED static const char *const s_rexmitstat_str[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; // [[using locked(m_RcvBufferLock)]] -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added @@ -10210,7 +10225,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', // the one that came in the input of this function. - for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end() && !m_bBroken; ++unitIt) { CUnit * u = *unitIt; CPacket &rpkt = u->m_Packet; @@ -10299,7 +10314,26 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } } - const int buffer_add_result = m_pRcvBuffer->insert(u); + CRcvBuffer::InsertInfo info = m_pRcvBuffer->insert(u); + + // Remember this value in order to CHECK if there's a need + // to request triggering TSBPD in case when TSBPD is in the + // state of waiting forever and wants to know if there's any + // possible time to wake up known earlier than that. + + // Note that in case of the "builtin group reader" (its own + // buffer), there's no need to do it here because it has also + // its own TSBPD thread. + + if (info.result == CRcvBuffer::InsertInfo::INSERTED) + { + // This may happen multiple times in the loop, so update only if earlier. + if (w_next_tsbpd == time_point() || w_next_tsbpd > info.first_time) + w_next_tsbpd = info.first_time; + w_new_inserted = true; + } + const int buffer_add_result = int(info.result); + if (buffer_add_result < 0) { // The insert() result is -1 if at the position evaluated from this packet's @@ -10310,8 +10344,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - w_new_inserted = true; - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) @@ -10621,6 +10653,8 @@ int srt::CUDT::processData(CUnit* in_unit) } #endif + // NULL time by default + time_point next_tsbpd_avail; bool new_inserted = false; if (m_PacketFilter) @@ -10649,6 +10683,7 @@ int srt::CUDT::processData(CUnit* in_unit) const int res = handleSocketPacketReception(incoming, (new_inserted), + (next_tsbpd_avail), (was_sent_in_order), (srt_loss_seqs)); @@ -10723,6 +10758,42 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + // 1. This is set to true in case when TSBPD during the last check + // has seen no packet candidate to ever deliver, hence it needs + // an update on that. Note that this is also false if TSBPD thread + // isn't running. + // 2. If next_tsbpd_avail is set, it means that in the buffer there is + // a new packet that precedes the previously earliest available packet. + // This means that if TSBPD was sleeping up to the time of this earliest + // delivery (after drop), this time we have received a packet to be delivered + // earlier than that, so we need to notify TSBPD immediately so that it + // updates this itself, not sleep until the previously set time. + + // The meaning of m_bTsbPdNeedsWakeup: + // - m_bTsbPdNeedsWakeup is set by TSBPD thread and means that it wishes to be woken up + // on every received packet. Hence we signal always if a new packet was inserted. + // - even if TSBPD doesn't wish to be woken up on every reception (because it sleeps + // until the play time of the next deliverable packet), it will be woken up when + // next_tsbpd_avail is set because it means this time is earlier than the time until + // which TSBPD sleeps, so it must be woken up prematurely. It might be more performant + // to simply update the sleeping end time of TSBPD, but there's no way to do it, so + // we simply wake TSBPD up and count on that it will update its sleeping settings. + + // XXX Consider: as CUniqueSync locks m_RecvLock, it means that the next instruction + // gets run only when TSBPD falls asleep again. Might be a good idea to record the + // TSBPD end sleeping time - as an alternative to m_bTsbPdNeedsWakeup - and after locking + // a mutex check this time again and compare it against next_tsbpd_avail; might be + // that if this difference is smaller than "dirac" (could be hard to reliably compare + // this time, unless it's set from this very value), there's no need to wake the TSBPD + // thread because it will wake up on time requirement at the right time anyway. + if (m_bTsbPd && ((m_bTsbPdNeedsWakeup && new_inserted) || next_tsbpd_avail != time_point())) + { + HLOGC(qrlog.Debug, log << "processData: will SIGNAL TSBPD for socket. WakeOnRecv=" << m_bTsbPdNeedsWakeup + << " new_inserted=" << new_inserted << " next_tsbpd_avail=" << FormatTime(next_tsbpd_avail)); + CUniqueSync tsbpd_cc(m_RecvLock, m_RcvTsbPdCond); + tsbpd_cc.notify_all(); + } + if (incoming.empty()) { // Treat as excessive. This is when a filter cumulates packets @@ -10738,16 +10809,6 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } - - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } - else - { - HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); - } } // Separately report loss records of those reported by a filter. @@ -10759,12 +10820,6 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); - - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } } // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it. diff --git a/srtcore/core.h b/srtcore/core.h index cc1f64789..02881cf9e 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -709,6 +709,7 @@ class CUDT void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + SRT_ATTR_EXCLUDES(m_RcvBufferLock) SRT_ATTR_REQUIRES(m_RecvAckLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); @@ -1008,7 +1009,7 @@ class CUDT SRT_ATTR_GUARDED_BY(m_RcvTsbPdStartupLock) 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_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. + sync::atomic m_bTsbPdNeedsWakeup; // Expected to wake up TSBPD when a read-ready data packet is received. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creation and joining. CallbackHolder m_cbAcceptHook; @@ -1151,16 +1152,17 @@ class CUDT /// /// @param incoming [in] The packet coming from the network medium /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_next_tsbpd [out] Get the TSBPD time of the earliest playable packet after insertion /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. /// /// @return 0 The call was successful (regardless if the packet was accepted or not). /// @return -1 The call has failed: no space left in the buffer. /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, sync::steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - /// Get the packet's TSBPD time - - /// the time when it is passed to the reading application. + // This function is to return the packet's play time (time when + // it is submitted to the reading application) of the given packet. /// The @a grp passed by void* is not used yet /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index ca8c365a3..c2e23e35a 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -417,7 +417,7 @@ struct DynamicStruct /// Fixed-size array template class. namespace srt { -template +template class FixedArray { public: @@ -433,37 +433,22 @@ class FixedArray } public: - const T& operator[](size_t index) const - { - if (index >= m_size) - throw_invalid_index(index); - - return m_entries[index]; - } - - T& operator[](size_t index) + const T& operator[](Indexer index) const { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } - const T& operator[](int index) const + T& operator[](Indexer index) { - if (index < 0 || static_cast(index) >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } - T& operator[](int index) - { - if (index < 0 || static_cast(index) >= m_size) - throw_invalid_index(index); - - return m_entries[index]; - } size_t size() const { return m_size; } diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 511c2dcb1..cd00f371c 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -76,7 +76,10 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } - return m_rcv_buffer->insert(unit); + auto info = m_rcv_buffer->insert(unit); + // XXX extra checks? + + return int(info.result); } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed @@ -643,6 +646,77 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } +TEST_F(CRcvBufferReadMsg, MsgOrderScraps) +{ + // Ok, in this test we're filling the message this way: + // 1. We have an empty packet in the first cell. + // 2. This is followed by a 5-packet message that is valid. + // 3. This is followed by empty, valid, empty, valid, valid packet, + // where all valid packets belong to the same message. + // 4. After that there should be 3-packet valid messsage. + // 5. We deploy drop request to that second scrapped message. + // 6. We read one message. Should be the first message. + // 7. We read one message. Should be the last message. + + auto& rcv_buffer = *m_rcv_buffer.get(); + + // 1, 2 + addMessage(5,// packets + 2, // msgno + m_init_seqno + 1, + true); + + // LAYOUT: 10 11 12 13 + // [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + // * (2 2 2 2 2) * 3 * 3 3) (4 4 4) + + // 3 + addPacket( + m_init_seqno + 7, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 9, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 10, + 3, + false, true, // last + true); + + // 4 + addMessage(3, // packets + 4, // msgno + m_init_seqno + 11, + true); + + // 5 + EXPECT_GT(rcv_buffer.dropMessage(m_init_seqno+8, m_init_seqno+8, 3, CRcvBuffer::KEEP_EXISTING), 0); + + // 6 + array buff; + SRT_MSGCTRL mc; + pair seqrange; + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*5); + EXPECT_EQ(mc.msgno, 2); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); + + CRcvBuffer::InsertInfo ii; + rcv_buffer.getAvailInfo((ii)); + EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); + + // 7 + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*3); + EXPECT_EQ(mc.msgno, 4); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); + +} + // One message (4 packets) is added to the buffer after a message with "in order" flag. // Read in order TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) From 695302da9f8f24145e6d0ff872f90502cb46d3ce Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 20 Mar 2025 17:10:07 +0100 Subject: [PATCH 165/174] [MAINT] Sonar: changed definition of the directories --- sonar-project.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 4bdaccdb2..afe611c78 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -28,8 +28,9 @@ sonar.links.issue=https://github.com/Haivision/srt/issues # ===================================================== # SQ standard properties -sonar.sources=srtcore/,apps/,common/,examples/,haicrypt/,scripts/,testing/ -sonar.tests=test/ +sonar.sources=srtcore/,common/,haicrypt/,scripts/ +sonar.tests=test/,testing/,apps/,examples/ +sonar.verbose=true # Properties specific to the C/C++ analyzer: #sonar.cfamily.build-wrapper-output=_build/sonar-output From 58a68dd43148575391ce820bbca85fd59d108f6e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 20 Mar 2025 17:19:51 +0100 Subject: [PATCH 166/174] Updated balancing documentation (#1226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated group-related documentation, including some part of balancing --------- Co-authored-by: Mikołaj Małecki Co-authored-by: stevomatthews --- docs/API/API-functions.md | 31 ++++++ docs/API/API.md | 16 ++- docs/features/bonding-intro.md | 8 ++ docs/features/socket-groups.md | 189 ++++++++++++++++++++++----------- 4 files changed, 180 insertions(+), 64 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index d74681ebc..adc05d7e4 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1301,6 +1301,28 @@ where: * `result`: result of the operation (if this operation recently updated this structure) * `token`: A token value set for that connection (see [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG)) +The weight is set to 0 by default by `srt_prepare_endpoint()` - you can set +it to a different value afterwards. The meaning of weight depends on the group +type: + +1. Backup groups: in this case it defines the link priority. The default 0 +value is the lowest priority and greater values declare higher priorities. The +priority for the backup groups determines which link is activated first when +the currently active link is unstable, and which should keep transmitting when +multiple active links are currently stable. + +2. Balancing groups with "fixed" algorithm: in this case it defines the +desired link load share. You can think of it as a percentage of link load, +but indeed a load percentage is defined as this weight value divided by a sum +of all weight values from all member links. Note however that the sum is +calculated out of all links that have been successfully connected. The +default 0 is also a special value that defines an "equalized" load share +(it's set to the arithmetic average of the weights from all links). + +The `SRT_SOCKGROUPDATA` structure is used in multiple purposes: + +* Prepare data for connection +* Getting the current member status [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1954,6 +1976,15 @@ the array; otherwise both fields are updated to reflect the current connection s of the group. For details, see the [SRT Connection Bonding: Quick Start](../features/bonding-intro.md) and [SRT Connection Bonding: Socket Groups](../features/socket-groups.md) documents. +For more information about `SRT_SOCKGROUPDATA` and obtaining the group +data, please refer to [srt_group_data](#srt_group_data). Note that the +group data filling by `srt_sendmsg2` and `srt_recvmsg2` calls differs in one +aspect to `srt_group_data`: member sockets that were found broken after the +operation will appear in the group data with `SRTS_BROKEN` state once after the +operation was done, although the sockets assigned to these members are already +closed and they are removed as members already. In case of `srt_group_data` +they will not appear at all. + **Helpers for [`SRT_MSGCTRL`](#SRT_MSGCTRL):** ``` diff --git a/docs/API/API.md b/docs/API/API.md index 3bd303830..24e721987 100644 --- a/docs/API/API.md +++ b/docs/API/API.md @@ -211,7 +211,7 @@ extra data for the operation. 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): +future use and should not be set other value than the initial one): - `srt_sendmsg2`: - `msgttl`: [IN] maximum time (in ms) to wait for successful delivery (-1: indefinitely) @@ -227,6 +227,20 @@ future use and should be 0): - `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 +- both above [IN] + - grpdata_size: size of the passed grpdata array + +- both above [OUT] + - grpdata: pointer to the array of group data to be filled by the call + - grpdata_size: actual size of the filled array + +**IMPORTANT**: no matter that fields are particularly marked as `[OUT]` (or +unused), values specified there could be used for input under certain +circumstances. Especially in `srt_sendmsg2` you should not reuse existing +objects of `SRT_MSGCTRL` type, but create always new ones and initialize them +with default `srt_msgctl_default` (or overwrite them first with it and set the +desired values anew). + 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 diff --git a/docs/features/bonding-intro.md b/docs/features/bonding-intro.md index d455afb57..dc4fac570 100644 --- a/docs/features/bonding-intro.md +++ b/docs/features/bonding-intro.md @@ -21,6 +21,14 @@ used. 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. +There are two main features for which the groups exist: + +1. Redundancy: keep the signal constantly delivered even if one of the +links gets unexpectedly broken. + +2. Balancing: send a stream with a bitrate that would be too high for +a single link, but enough if every link gets only a share of load. + To learn more about socket groups and their abilities, please read the [SRT Socket Groups](socket-groups.md) document. diff --git a/docs/features/socket-groups.md b/docs/features/socket-groups.md index cef0ee98e..2afa594be 100644 --- a/docs/features/socket-groups.md +++ b/docs/features/socket-groups.md @@ -30,11 +30,48 @@ The groups types generally split into two categories: This category contains currently only one Multicast type (**CONCEPT! NOT IMPLEMENTED!**). Multicast group has a behavior dependent on the connection side and it is - predicted to be only used in case when the listener side is a stream sender + intended to be used only in the case when the listener side is a stream sender with possibly multiple callers being stream receivers. It utilizes the UDP multicast feature in order to send payloads, while the control communication is still sent over the unicast link. +From the application point of view it is important to remember several rules +concerning groups: + +1. On the caller side the group has to be created, like a socket, and then it's + ready to connect. In distinction to socket, you can connect the group multiple + times. The group is considered connected, if at least one connection has + been successfully established, then other connections can be added at any time. + +2. On the listener side you create the listener socket, and you call + the `srt_accept` function, from which you get the group ID, if that listener + socket has received a group connection request. Once accepted, you get the + connected group this way and every next connection is handled in the background. + +3. Disconnected links are removed from the group and are not reconnected. The + application simply has to connect that link again, if it chooses to do so. + +4. You can remove a single link from the group by simply closing the member + socket. This socket is provided in the group member status table together + with other data that allow to identify particular link. + +In other words, links in socket groups are never "defined" - they can only be +"established". When they get broken, they are simply removed from the group. +It's up to the application to re-establish them. + +The group members can be also in appropriate states. The freshly created member +that is in the process of connecting is in "pending" state. When the connection +succeeds, it's in "idle" state. Then, when it's used for transmission, it's in +"active" state. If an operation on the link fails at any stage, it is removed +from the group. + +In Broadcast and Balancing group types, the "idle" links are activated once +they are found ready for sending as well as they report readiness for reading - +"idle" is only a temporary state between being freshly connected and being used +for transmission. In case of Main/Backup groups, the "idle" state is usually +more permanent and is only turned to "active" when necessary, while it can be +as well put back to "idle". + ## Details for the Group Types ### 1. Broadcast @@ -55,28 +92,46 @@ Every next link in this group gives then another 100% overhead. ### 2. Main/Backup -This solution is more complicated and more challenging for the settings, -and in contradiction to Broadcast group, it costs some penalties. - -In this group, only one link out of member links is used for transmission -in a normal situation. Other links may start being used when there's happening -an event of "disturbance" on a link, which makes it considered "unstable". This -term is introduced beside "broken" because SRT normally uses 5 seconds to be -sure that the link is broken, and this is way too much to be used as a latency -penalty, if you still want to have a relatively low latency. - -Because of that there's a configurable timeout (with `SRTO_GROUPSTABTIMEO` -option), which is the maximum time distance between two consecutive responses -sent from the receiver back to the sender. If this time was exceeded, the link -is considered unstable. This can mean either some short-living minor -disturbance, as well as that the link is broken, just SRT hasn't a proof of -that yet. - -At the moment when one link becomes unstable, another link is immediately -activated, and all packets that have been kept in the sender buffer since -the last ACK are first sent. Since this moment there are two links active -until the moment when the matter finally resolves - either the unstable -link will become stable again, or it will be broken. +The configuration of this type of groups is somewhat more complicated than with +the other group types. In particular, it may be challenging to arrive at the +optimal settings for a given set of network conditions and desired latency. +Unlike Broadcast group type, there are some penalties, but there are also +advantages. Whereas the overhead for redundancy in the case of Broadcast groups +is 100% per every next redundant link, this is usually kept at a negligible +minimum for Main/Backup groups. + +The idea of the Main/Backup group is to use only one link for transmission +of the data, but be ready to quickly activate the other links, if it turns +out that the currently used link is "likely broken" (by not having received any +packet from the peer for a given timeout). The unstable state is stricter than +broken connection: while broken connection is recognized by response time +exceeding the "peer idle" timeout (`SRTO_PEERIDLETIMEO`, default: 5s), the +unstable state is recognized by exceeding the "group stability" timeout, +which is 10ms of the ACK period with addition of some jitter tolerance (this +value is dependent on the current latency and average-tolerated RTT and the +minimum can be controlled by `SRTO_GROUPMINSTABLETIMEO`). As this still doesn't +mean broken, the transmission continues over multiple links since that time. +Activation of a link means that all packets since the last ACK sequence +are first sent over this link, then it continues with ongoing packets, so that, +if everything goes well (the new link is successfully keeping up with the pace +and any packet loss caused by the initial burst is recovered), the application +should see completely no disturbance due to this new link activation. + +Note that there doesn't happen anything like "switching" of the link. You should +rather think of it as turning into a "temporary broadcast" mode, where multiple +links are used for transmission (still, only for those links that were activated). +This situation can then get resolved into one of the following: + +* The link turns back to stable, so there are multiple stable links +* The link gets really broken, so only the newly activated link transmits +* The link is unstable for too long, so it is forcefully closed + +In the first case we have then continued the "temporary broadcast" mode. In +this situation, after a short cooldown time, out of all currently active links +there is selected one that is considered the "best" (where priority matters, +but also the response jitter is taken into account) and this one continues +with the transmission, while all others are "silenced", that is, transmission +over these links is stopped. The state maintenance always keep up to the following rules: @@ -87,8 +142,8 @@ and remains ready to take over if there is a necessity. b) Unstable links continue to be used no matter that it may mean parallel sending for a short time. This state should last at most as long as it takes -for SRT to determie the link broken - either by getting the link broken by -itself, or by closing the link when it's remaining unstable too long time. +for SRT to determine the link broken - either by breaking the link by +itself, or by closing the link when it has been unstable for too long. This mode allows also to set link priorities - the greater, the more preferred. This priority decides mainly, which link is "best" and which is selected to @@ -144,45 +199,53 @@ any quite probable packet loss that may occur during this process. The idea of balancing means that there are multiple network links used for carrying out the same transmission, however a single input signal should distribute the incoming packets between the links so that one link can -leverage the bandwidth burden of the other. Note that this group is not -directly used as protection - it is normally intended to work with a +leverage the bandwidth burden of the other. Note that this group only +partially can provide the redundancy - it is normally intended to work with a condition that a single link out of all links in the group would not be -able to withstand the bitrate of the signal. In order to utilize a -protection, the mechanism should quickly detect a link as broken so -that packets lost on the broken link can be resent over the others, -but no such mechanism has been provided for balancing group. - -As there could be various ways as to how to implement balancing -algorithm, there's a framework provided to implement various methods, -and two algorithms are currently provided: - -1. `plain` (default). This is a simple round-robin - next link selected -to send the next packet is the oldest used so far. - -2. `window`. This algorithm is performing cyclic measurement of the +able to withstand the bitrate of the signal. So, to stay safe, you need +to make sure that you always have one link that provides the excessive +capacity so that breaking one link doesn't lower the overall capacity +below the requirement for the signal's bitrate. + +Please also keep in mind that the group is considered connected when +it contains at least one connected member link. This means also that the +group becomes ready for transmission after connecting the first link, +as well as it remains ready even if some member links get broken. So, +if the application wants to make sure that a transmission is balanced between +links (where only together can they maintain the bandwidth capacity required +for a signal), it must make sure that all "required" links are established by +monitoring the group data. For example, if you need a minimum of 3 links to +balance the load, you should delay starting the transmission until all 3 links +are established (that is, all of them report "idle" state), and also stop it +(or quickly reconfigure the stream to a lower bandwidth) in case when a broken +link caused that the others do not cover the required capacity. + +As there could be more than one way to implement a balancing algorithm, there +is a framework for implementing various methods, so that new algorithms are +easier to provide in future. Currently there are two algorithms provided: + +1. `fixed`. This is based on the simple round-robin method, but the usage +of particular link grows invertedly towards the share value, which is +controlled by the `weight` parameter (that is, a link with more weight can +be proportionally more burdened). You can easily think of the weight values as +a percentage of load burden for particular link - however in reality the share +of the load is calculated as a percentage that particular link's weight +comprises among the sum of all weight values. Additionally, a value of 0 is +special and it is translated into the arithmetic average of all non-zero +weighted links, and if all links have weight 0, all links have equal share. Be +careful here though with the non-established and broken links. For example, if +you have 3 links with weight 10, 20 and 30, it results in a load balance of +16.6%, 33.3% and 50% respectively. However if the second link gets broken, +there are then 2 links with 10 and 30, which results in load balance of 25% and +75% respectively. + +2. `window` (default). This algorithm performs cyclic measurement of the minimum flight window and this way determines the "cost of sending" -of a packet over particular link. The link is then "paid" for sending -a packet appropriate "price", which is collected in the link's "pocket". -To send the next packet the link with lowest state of the "pocket" is -selected. The "cost of sending" measurement is being repeated once per -a time with a distance of 16 packets on each link. - -There are possible also other methods and algorithms, like: - -a) Explicit share definition. You declare, how much bandwidth you expect -the links to withstand as a percentage of the signal's bitrate. This -shall not exceed 100%. This is merely like the above Window algorithm, -but the "cost of sending" is defined by this percentage. - -b) Bandwidth measurement. This relies on the fact that the current -sending on particular link should use only some percentage of its -overall possible bandwidth. This requires a reliable way of measuring -the bandwidth, which is currently not good enough yet. This needs to -use a similar method as in "window" algorithm, that is, start with -equal round-robin and then perform actively a measurement and update -the cost of sending by assigning so much of a share of the signal -bitrte as it is represented by the share of the link in the sum of -all maximum bandwidth values from every link. +of a packet over a particular link (the bigger the flight span of the link, +the higher the sending cost). This evaluated cost is then added to the current +burden state of the link, and then the link with lowest burden is selected to +send the next packet. The "cost of sending" measurement is being repeated once +per a time at an interval of 16 packets on each link. ### 4. Multicast (**CONCEPT! NOT IMPLEMENTED!**) @@ -192,7 +255,7 @@ receiving a data stream sent from a stream server by multiple receivers. Multicast sending is using the feature of UDP multicast, however the connection concept is still in force. The concept of multicast groups -is predicted to facilitate the multicast abilities provided by the router +is intended to facilitate the multicast abilities provided by the router in the LAN, while still maintain the advantages of SRT. When you look at the difference that UDP multicast provides you towards From f60773c9f842ead99633cb9a3196e1797a403295 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 21 Mar 2025 10:09:14 +0100 Subject: [PATCH 167/174] [core][apps][doc] Implemented the socket close reason feature (#2747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core][apps][doc] Implemented the socket close reason feautre --------- Co-authored-by: Mikołaj Małecki --- docs/API/API-functions.md | 196 ++++++++++++++++++++++++++++++++++++-- srtcore/api.cpp | 149 ++++++++++++++++++++++++++--- srtcore/api.h | 27 +++++- srtcore/atomic_clock.h | 6 ++ srtcore/core.cpp | 86 +++++++++++++++-- srtcore/core.h | 26 ++++- srtcore/group.cpp | 5 +- srtcore/packet.cpp | 2 +- srtcore/queue.cpp | 3 +- srtcore/srt.h | 33 +++++++ srtcore/srt_c_api.cpp | 29 +++++- testing/testmedia.cpp | 92 ++++++++++++++++-- 12 files changed, 604 insertions(+), 50 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index adc05d7e4..ff2090232 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -21,6 +21,7 @@ | [srt_getsndbuffer](#srt_getsndbuffer) | Retrieves information about the sender buffer | | [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) | Retrieves the information about the maximum payload size in a single packet | | [srt_close](#srt_close) | Closes the socket or group and frees all used resources | +| [srt_close_withreason](#srt_close_withreason) | Closes the socket or group and frees all used resources (with setting the reason code) | | | |

Connecting

@@ -150,6 +151,7 @@ Since SRT v1.5.0. | [srt_rejectreason_str](#srt_rejectreason_str) | Returns a constant string for the reason of the connection rejected, as per given code ID | | [srt_setrejectreason](#srt_setrejectreason) | Sets the rejection code on the socket | | [srt_getrejectreason](#srt_getrejectreason) | Provides a detailed reason for a failed connection attempt | +| [srt_close_getreason](#srt_close_getreason) | Provides a detailed reason for closing a socket | | | |

Rejection Reasons

@@ -323,6 +325,7 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne * [srt_getsndbuffer](#srt_getsndbuffer) * [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) * [srt_close](#srt_close) +* [srt_close_withreason](#srt_close_withreason) ### srt_socket @@ -623,16 +626,22 @@ With default options this value should be 1456 for IPv4 and 1444 for IPv6. --- -### srt_close +### srt_close, srt_close_withreason ``` SRTSTATUS srt_close(SRTSOCKET u); +SRTSTATUS srt_close_withreason(SRTSOCKET u, int reason); ``` Closes the socket or group and frees all used resources. Note that underlying UDP sockets may be shared between sockets, so these are freed only with the last user closed. +**Arguments**: + +* `u`: Socket or group to close +* `reason`: Reason code for closing. You should use numbers from `SRT_CLSC_USER` up. + | Errors | | |:------------------------------- |:----------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -2924,6 +2933,7 @@ to timestamp packets submitted to SRT is not recommended and must be done with a * [srt_getrejectreason](#srt_getrejectreason) * [srt_rejectreason_str](#srt_rejectreason_str) * [srt_setrejectreason](#srt_setrejectreason) +* [srt_close_getreason](#srt_close_getreason) General notes concerning the `getlasterror` diagnostic functions: when an API function ends up with error, this error information is stored in a thread-local @@ -3060,6 +3070,38 @@ a numeric code, which can be translated into a message by The returned value is one of the values listed in enum `SRT_REJECT_REASON`. For an invalid value of `sock` the `SRT_REJ_UNKNOWN` is returned. +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### srt_close_getreason + +``` +int srt_close_getreason(SRTSOCKET u, SRT_CLOSE_INFO* info); +``` + +Retrieves the reason code for closing the socket. This designates the +very first reason of closing the socket or group (if there could have +been multiple reasons for closing it, only the first one counts). + +Note that this information may be retrieved even if the socket is already +physically closed, but only for up to 10 seconds after that happens (more +precisely, 10 cycles of GC, which run every 1 second) and only up to 10 such +records are remembered (newer closed ones push off the oldest one). + +**Arguments**: + +* `u`: Socket or group that you believe is closed or broken +* `info`: The structure where the reason is written, see [`SRT_CLOSE_INFO`](#srt_close_info) + +Returns 0 in case of success. Returns `SRT_ERROR` (-1) in case of error, +which may be because: + +* `info` is a NULL pointer +* `u` is an `SRT_INVALID_SOCK` value +* `u` was not found in the closed socket database (expired or was pushed off) + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -3203,6 +3245,136 @@ exceeds the free space in a single packet that results from the value of the negotiated MSS value +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### `SRT_CLOSE_INFO` + +This structure can be used to get the closing reason (simplified definition): + +``` +struct SRT_CLOSE_INFO +{ + SRT_CLOSE_REASON agent; + SRT_CLOSE_REASON peer; + int64_t time; +}; +``` + +Where: + +* `agent`: The reason code set on the agent ("this machine") side of the connection +if the very first reason of closing has happened on the agent. If the closing was +initiated by the peer, this field contains `SRT_CLS_PEER` value + +* `peer`: If `agent` == `SRT_CLS_PEER`, then closing was initiated by peer and this +field contains the value of this reason + +* `time`: Time when closing has happened, in the same convention as the time value +supplied by [`srt_time_now`](#srt_time_now) + +The values for `agent` and `peer` can be internal, out of the below shown list, or +it can be a user code, if the value is at least `SRT_CLSC_USER`. + + +### Closing reasons + +#### SRT_CLS_UNKNOWN + +The reason not set. The value is used as a fallback if the reason wasn't properly set. + +#### SRT_CLS_INTERNAL + +Closed by internal reasons during connection attempt. + +#### SRT_CLS_PEER + +Closed by the peer (the value is to be used on agent). This happens when the closing +action has been initiated by peer through sending the `UMSG_SHUTDOWN` message. + +#### SRT_CLS_RESOURCE + +A problem with resource allocation. + +#### SRT_CLS_ROGUE + +Received wrong data in the packet. This happens when the socket was closed +due to security reasons, when the data in the packet do not match the expected +protocol specification. + +#### SRT_CLS_OVERFLOW + +Emergency close due to receiver buffer's double overflow that has lead to +an irrecoverable situation. This happens when too slow reading data by the +application has caused that first, incoming packets cannot be inserted into +the buffer because the position in the buffer mapped to their sequence number +locates them outside the buffer. If this situation isn't quickly recovered +from, it causes eventually that the sequence number distance between the last +packet still stored in the buffer and the newly incoming packet exceeds the +size of the receiver buffer. This is then an irrecoverable situation and in +result the socket is closed with this code as a reason. + +#### SRT_CLS_IPE + +Internal program error. Currently used if the incoming acknowledge packet +represents the sequence number that has never been sent, or the value is +out of any valid range. + +#### SRT_CLS_API + +The socket has been closed by the API call of `srt_close()`. This code +is set also if the reason value used in `srt_close_withreason()` is +less than `SRT_CLSC_USER`. + +#### SRT_CLS_FALLBACK + +This value is set on the `peer` field in case when the peer runs the SRT +version that does not support this feature. If this feature is supported, +then the peer should send `UMSG_SHUTDOWN` message with the reason value, +which will be then set on the `peer` field. + +#### SRT_CLS_LATE + +Accepted-socket late-rejection or in-handshake rollback. The late rejection +is something that may happen when the listener side responds to the caller +with a proper handshake message, but the caller rejects that message by +some reason. This way, the caller gets closed with a rejection reason, but +at this moment the accepted socket on the listener side considers itself +connected. Therefore the caller socket in this situation sends first +the `UMSG_SHUTDOWN` message to the peer (that is, the accepted socket) +and in result the accepted socket gets closed with this reason code. + +#### SRT_CLS_CLEANUP + +All sockets are being closed due to srt_cleanup() call. + +#### SRT_CLS_DEADLSN + +This is an accepted socket off a dead listener. If the listener +socket has been closed before the accepted socket could be completed, +the socket can be returned as valid, but could not be used due to +having the listener socket closed too early. + +#### SRT_CLS_PEERIDLE + +Peer didn't send any packet for a time of `SRTO_PEERIDLETIMEO`. This +means that the peer idle timeout has been reached while waiting for +any packet incoming from the peer. + +#### SRT_CLS_UNSTABLE + +Requested to be broken as unstable in Backup group. This happens +exclusively in the group of type `SRT_GTYPE_BACKUP` in case when +the link that was used so far for transmission, has become too +slowly responsive, which caused activation of one of the backup +links, and then this link didn't get back to stability in a given +time (the minimum is configured in `SRTO_GROUPMINSTABLETIMEO`), +which caused that the newly activated link has taken over transmission +and the socket using the unstable link has been closed with this +reason code. + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -3473,9 +3645,18 @@ 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. +The binding you are attempting to set up a socket with, using the `srt_bind` +call, cannot be completed because it conflicts with another existing binding. + +An attempt of binding a socket, in the conditions of having some other socket already +bound to the same port number, can result in one of three possibilities: + +1. The binding is separate to the existing one (succeeds). +2. The binding intersects with the exiting one (fails). +3. The binding is exactly identical to the existing one (see below). + +See the [`srt_bind`](#srt_bind) for a reference about what kinds of binding +addresses can coexist without conflicts. 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 @@ -3511,10 +3692,11 @@ address that is enclosed by this existing binding is exactly identical to the specified one and all of the following conditions must be satisfied between them: -1. The `SRTO_REUSEADDR` must be true (default) in both. +1. The `SRTO_REUSEADDR` must be true (default) in both the attempted and +existing bindings. -2. The IP address specification (in case of IPv6, also including the value of -`SRTO_IPV6ONLY` flag) must be exactly identical. +2. The IP address specification, also as a wildcard (in case of IPv6, also +including the value of `SRTO_IPV6ONLY` flag), must be exactly identical. 3. The UDP-specific settings (SRT options that map to UDP options) must be identical. diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ede03aad5..4023cf9a4 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -117,14 +117,14 @@ SRT_SOCKSTATUS srt::CUDTSocket::getStatus() } // [[using locked(m_GlobControlLock)]] -void srt::CUDTSocket::breakSocket_LOCKED() +void srt::CUDTSocket::breakSocket_LOCKED(int reason) { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. m_UDT.m_bBroken = true; m_UDT.m_iBrokenCounter = 0; HLOGC(smlog.Debug, log << "@" << m_SocketID << " CLOSING AS SOCKET"); - m_UDT.closeInternal(); + m_UDT.closeInternal(reason); setClosed(); } @@ -283,10 +283,14 @@ void srt::CUDTUnited::closeAllSockets() { ScopedLock glock(m_GlobControlLock); + // Do not do generative expiry removal - there's no chance + // anyone can extract the close reason information since this point on. + m_ClosedDatabase.clear(); + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++i) { CUDTSocket* s = i->second; - s->breakSocket_LOCKED(); + s->breakSocket_LOCKED(SRT_CLS_CLEANUP); #if ENABLE_BONDING if (s->m_GroupOf) @@ -855,7 +859,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listener, #endif SRTSOCKET id = ns->m_SocketID; - ns->core().closeInternal(); + ns->core().closeInternal(SRT_CLS_LATE); ns->setClosed(); // The mapped socket should be now unmapped to preserve the situation that @@ -1018,6 +1022,37 @@ SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } +SRTSTATUS srt::CUDTUnited::getCloseReason(const SRTSOCKET u, SRT_CLOSE_INFO& info) +{ + // protects the m_Sockets structure + ScopedLock cg(m_GlobControlLock); + + // We need to search for the socket in: + // m_Sockets, if it is somehow still alive, + // m_ClosedSockets, if it's when it should be, + // m_ClosedDatabase, if it has been already garbage-collected and deleted. + + sockets_t::const_iterator i = m_Sockets.find(u); + if (i != m_Sockets.end()) + { + i->second->core().copyCloseInfo((info)); + return SRT_STATUS_OK; + } + + i = m_ClosedSockets.find(u); + if (i != m_ClosedSockets.end()) + { + i->second->core().copyCloseInfo((info)); + } + + map::iterator c = m_ClosedDatabase.find(u); + if (c == m_ClosedDatabase.end()) + return SRT_ERROR; + + info = c->second.info; + return SRT_STATUS_OK; +} + SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -2007,7 +2042,7 @@ SRTSOCKET srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targ continue; // This will also automatically remove it from the group and all eids - close(s); + close(s, SRT_CLS_INTERNAL); } // There's no possibility to report a problem on every connection @@ -2089,7 +2124,7 @@ void srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, } } -SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) +SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u, int reason) { #if ENABLE_BONDING if (CUDT::isgroup(u)) @@ -2121,7 +2156,7 @@ SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) IF_HEAVY_LOGGING(ScopedExitLog slog(k.socket)); HLOGC(smlog.Debug, log << "CUDTUnited::close/begin: @" << u << " busy=" << k.socket->isStillBusy()); - return close(k.socket); + return close(k.socket, reason); } #if ENABLE_BONDING @@ -2172,7 +2207,54 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::recordCloseReason(CUDTSocket* s) +{ + CloseInfo ci; + ci.info.agent = SRT_CLOSE_REASON(s->core().m_AgentCloseReason.load()); + ci.info.peer = SRT_CLOSE_REASON(s->core().m_PeerCloseReason.load()); + ci.info.time = s->core().m_CloseTimeStamp.load().time_since_epoch().count(); + + m_ClosedDatabase[s->m_SocketID] = ci; + + // As a DOS attack prevention, do not allow to keep more than 10 records. + // In a normal functioning of the application this shouldn't be necessary, + // but it is still needed that a record of a dead socket is kept for + // 10 gc cycles more to ensure that the application can obtain it even after + // the socket has been physically removed. But if we don't limit the number + // of these records, this could be vulnerable for DOS attack if the user + // forces the application to create and close SRT sockets very quickly. + // Hence remove the oldest record, which can be recognized from the `time` + // field, if the number of records exceeds 10. + if (m_ClosedDatabase.size() > MAX_CLOSE_RECORD_SIZE) + { + // remove the oldest one + // This can only be done by collecting all time info + map which; + + for (map::iterator x = m_ClosedDatabase.begin(); + x != m_ClosedDatabase.end(); ++x) + { + which[x->second.info.time] = x->first; + } + + map::iterator y = which.begin(); + size_t ntodel = m_ClosedDatabase.size() - MAX_CLOSE_RECORD_SIZE; + for (size_t i = 0; i < ntodel; ++i) + { + // Sanity check - should never happen because it's unlikely + // that two different sockets were closed exactly at the same + // nanosecond time. + if (y == which.end()) + break; + + m_ClosedDatabase.erase(y->second); + ++y; + } + } +} + +SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s, int reason) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); @@ -2242,6 +2324,8 @@ SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) // broadcast all "accept" waiting CSync::lock_notify_all(s->m_AcceptCond, s->m_AcceptLock); + + s->core().setAgentCloseReason(reason); } else { @@ -2251,7 +2335,7 @@ SRTSTATUS 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->core().closeInternal(); + s->core().closeInternal(reason); // synchronize with garbage collection. HLOGC(smlog.Debug, @@ -2286,6 +2370,8 @@ SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) } #endif + recordCloseReason(s); + // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(s->m_SocketID, s->core().m_sPollID); @@ -2964,6 +3050,12 @@ void srt::CUDTUnited::checkBrokenSockets() HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); + // Note that this will not override the value that has been already + // set by some other functionality, only set it when not yet set. + s->core().setAgentCloseReason(SRT_CLS_INTERNAL); + + recordCloseReason(s); + // close broken connections and start removal timer s->setClosed(); tbc.push_back(i->first); @@ -3111,7 +3203,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; - as->breakSocket_LOCKED(); + as->breakSocket_LOCKED(SRT_CLS_DEADLSN); // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(as->m_SocketID, as->core().m_sPollID); @@ -3153,7 +3245,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); leaveCS(m_GlobControlLock); - s->core().closeInternal(); + s->core().closeInternal(SRT_CLS_INTERNAL); enterCS(m_GlobControlLock); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; @@ -3194,6 +3286,31 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) } } +void srt::CUDTUnited::checkTemporaryDatabases() +{ + ScopedLock cg(m_GlobControlLock); + + // It's not very efficient to collect first the keys of all + // elements to remove and then remove from the map by key. + + // In C++20 this is possible by doing + // m_ClosedDatabase.erase_if([](auto& c) { return --c.generation <= 0; }); + // but nothing equivalent in the earlier standards. + + vector expired; + + for (map::iterator c = m_ClosedDatabase.begin(); + c != m_ClosedDatabase.end(); ++c) + { + --c->second.generation; + if (c->second.generation <= 0) + expired.push_back(c->first); + } + + for (vector::iterator i = expired.begin(); i != expired.end(); ++i) + m_ClosedDatabase.erase(*i); +} + void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { w_m.m_mcfg = s->core().m_config; @@ -3656,14 +3773,18 @@ void* srt::CUDTUnited::garbageCollect(void* p) UniqueLock gclock(self->m_GCStopLock); + // START LIBRARY RUNNING LOOP while (!self->m_bClosing) { INCREMENT_THREAD_ITERATIONS(); self->checkBrokenSockets(); + self->checkTemporaryDatabases(); HLOGC(inlog.Debug, log << "GC: sleep 1 s"); self->m_GCStopCond.wait_for(gclock, seconds_from(1)); } + // END. + THREAD_EXIT(); return NULL; } @@ -4021,11 +4142,11 @@ SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int } } -SRTSTATUS srt::CUDT::close(SRTSOCKET u) +SRTSTATUS srt::CUDT::close(SRTSOCKET u, int reason) { try { - return uglobal().close(u); + return uglobal().close(u, reason); } catch (const CUDTException& e) { @@ -4749,7 +4870,7 @@ SRTSOCKET connect(SRTSOCKET u, const struct sockaddr* name, int namelen) SRTSTATUS close(SRTSOCKET u) { - return srt::CUDT::close(u); + return srt::CUDT::close(u, SRT_CLS_API); } SRTSTATUS getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) diff --git a/srtcore/api.h b/srtcore/api.h index 7af4873a6..850af09a6 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -202,7 +202,7 @@ class CUDTSocket /// from within the GC thread only (that is, only when /// the socket should be no longer visible in the /// connection, including for sending remaining data). - void breakSocket_LOCKED(); + void breakSocket_LOCKED(int reason); /// This makes the socket no longer capable of performing any transmission /// operation, but continues to be responsive in the connection in order @@ -249,6 +249,8 @@ class CUDTUnited // Public constants static const int32_t MAX_SOCKET_VAL = SRTGROUP_MASK - 1; // maximum value for a regular socket + static const int MAX_CLOSE_RECORD_TTL = 10; + static const size_t MAX_CLOSE_RECORD_SIZE = 10; public: enum ErrorHandling @@ -317,8 +319,8 @@ class CUDTUnited SRTSOCKET groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); SRTSOCKET singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); #endif - SRTSTATUS close(const SRTSOCKET u); - SRTSTATUS close(CUDTSocket* s); + SRTSTATUS close(const SRTSOCKET u, int reason); + SRTSTATUS close(CUDTSocket* s, int reason); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockdevname(const SRTSOCKET u, char* name, size_t* namelen); @@ -575,6 +577,25 @@ class CUDTUnited CEPoll m_EPoll; // handling epoll data structures and events + struct CloseInfo + { + SRT_CLOSE_INFO info; + int generation; + + // The value here defines how many GC rolls it takes + // to remove the record. As GC rolls every 1 second, + // this is more-less the number of seconds this record + // will be alive AFTER you close the socket. + CloseInfo(): generation(MAX_CLOSE_RECORD_TTL) {} + }; + std::map m_ClosedDatabase; + + void checkTemporaryDatabases(); + void recordCloseReason(CUDTSocket* s); + +public: + SRTSTATUS getCloseReason(const SRTSOCKET u, SRT_CLOSE_INFO& info); + private: CUDTUnited(const CUDTUnited&); CUDTUnited& operator=(const CUDTUnited&); diff --git a/srtcore/atomic_clock.h b/srtcore/atomic_clock.h index e01012313..840d35252 100644 --- a/srtcore/atomic_clock.h +++ b/srtcore/atomic_clock.h @@ -73,6 +73,12 @@ class AtomicClock dur.store(uint64_t(d.time_since_epoch().count())); } + void compare_exchange(const time_point_type& exp, const time_point_type& toset) + { + uint64_t val = exp.time_since_epoch().count(); + dur.compare_exchange(val, toset.time_since_epoch().count()); + } + AtomicClock& operator=(const time_point_type& s) { dur = s.time_since_epoch().count(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ebc881c2a..d1bd8c4ad 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3793,7 +3793,9 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { HLOGC(cnlog.Debug, log << CONID() << "startConnect: REJECTED by processConnectResponse - sending SHUTDOWN"); - sendCtrl(UMSG_SHUTDOWN); + setAgentCloseReason(SRT_CLS_LATE); + uint32_t reason[1] = { SRT_CLS_LATE }; + sendCtrl(UMSG_SHUTDOWN, NULL, reason, sizeof reason); } if (cst != CONN_CONTINUE && cst != CONN_CONFUSED) @@ -6338,7 +6340,7 @@ void srt::CUDT::addressAndSend(CPacket& w_pkt) // [[using maybe_locked(m_GlobControlLock, if called from breakSocket_LOCKED, usually from GC)]] // [[using maybe_locked(m_parent->m_ControlLock, if called from srt_close())]] -bool srt::CUDT::closeInternal() ATR_NOEXCEPT +bool srt::CUDT::closeInternal(int reason) ATR_NOEXCEPT { // NOTE: this function is called from within the garbage collector thread. @@ -6390,6 +6392,14 @@ bool srt::CUDT::closeInternal() ATR_NOEXCEPT } } + // Some calls of closeInternal pass UNKNOWN here, which means + // that they don't want to change the code. It should have been + // set already somewhere else, however. + if (reason != SRT_CLS_UNKNOWN) + { + setAgentCloseReason(reason); + } + // remove this socket from the snd queue if (m_bConnected) m_pSndQueue->m_pSndUList->remove(this); @@ -6437,7 +6447,8 @@ bool srt::CUDT::closeInternal() ATR_NOEXCEPT if (!m_bShutdown) { HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer @" << m_PeerID); - sendCtrl(UMSG_SHUTDOWN); + int32_t shdata[1] = { reason }; + sendCtrl(UMSG_SHUTDOWN, NULL, shdata, sizeof shdata); } // Store current connection information. @@ -8087,7 +8098,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_SHUTDOWN: // 101 - Shutdown if (m_PeerID == SRT_SOCKID_CONNREQ) // Dont't send SHUTDOWN if we don't know peer ID. break; - ctrlpkt.pack(pkttype); + ctrlpkt.pack(pkttype, NULL, rparam, size); ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); @@ -8596,6 +8607,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); m_bBroken = true; m_iBrokenCounter = 0; + setAgentCloseReason(SRT_CLS_IPE); updateBrokenConnection(); completeBrokenConnectionDependencies(SRT_ESECFAIL); // LOCKS! @@ -9029,6 +9041,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // this should not happen: attack or bug m_bBroken = true; m_iBrokenCounter = 0; + setAgentCloseReason(SRT_CLS_ROGUE); updateBrokenConnection(); completeBrokenConnectionDependencies(SRT_ESECFAIL); // LOCKS! @@ -9256,8 +9269,31 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void srt::CUDT::processCtrlShutdown() +void srt::CUDT::processCtrlShutdown(const CPacket& ctrlpkt) { + const uint32_t* data = (const uint32_t*) ctrlpkt.m_pcData; + const size_t data_len = ctrlpkt.getLength() / 4; + + int reason = 0; + + // This condition should be ALWAYS satisfied, it's only + // a sanity check before reading the data. Versions that + // do not support close reason will simply send 0 here because + // it's the padding 0 that is provided in every command + // that is not expected to carry any "body". It is acceptable + // that the old versions simply send 0 here, but then you + // can't have the UNKNOWN value in any of close reason + // fields because it means that it wasn't set. + if (data_len > 0) + { + reason = data[0]; + } + + if (reason == 0) + { + setPeerCloseReason(SRT_CLS_FALLBACK); + } + m_bShutdown = true; m_bClosing = true; m_bBroken = true; @@ -9343,7 +9379,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - processCtrlShutdown(); + processCtrlShutdown(ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request @@ -10064,10 +10100,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) return true; } -// This is a close request, but called from the +// This is a close request, but called from the handler of the +// buffer overflow in live mode. void srt::CUDT::processClose() { - sendCtrl(UMSG_SHUTDOWN); + uint32_t res[1] = { SRT_CLS_OVERFLOW }; + sendCtrl(UMSG_SHUTDOWN, NULL, res, sizeof res); m_bShutdown = true; m_bClosing = true; @@ -11697,6 +11735,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && (currtime - last_rsp_time > microseconds_from(PEER_IDLE_TMO_US)))) { + setAgentCloseReason(SRT_CLS_PEERIDLE); // // Connection is broken. // UDT does not signal any information about this instead of to stop quietly. @@ -12211,3 +12250,34 @@ void srt::CUDT::processKeepalive(const CPacket& ctrlpkt, const time_point& tsArr if (m_config.bDriftTracer) m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, -1); } + +// This function should be called when closing the socket internally. +void srt::CUDT::setAgentCloseReason(int reason) +{ + m_AgentCloseReason.compare_exchange(SRT_CLS_UNKNOWN, reason); + + // Do not touch m_PeerCloseReason, it should remain SRT_CLS_UNKNOWN. + // If this reason is already set to some value, then m_AgentCloseReason + // should have been already set to SRT_CLS_PEER. + + m_CloseTimeStamp.compare_exchange(time_point(), steady_clock::now()); +} + +// This function should be called in a handler of UMSG_SHUTDOWN. +void srt::CUDT::setPeerCloseReason(int reason) +{ + m_AgentCloseReason.compare_exchange(SRT_CLS_UNKNOWN, SRT_CLS_PEER); + if (m_AgentCloseReason == SRT_CLS_PEER) + { + m_PeerCloseReason.compare_exchange(SRT_CLS_UNKNOWN, reason); + + m_CloseTimeStamp.compare_exchange(time_point(), steady_clock::now()); + } +} + +void srt::CUDT::copyCloseInfo(SRT_CLOSE_INFO& info) +{ + info.agent = SRT_CLOSE_REASON(m_AgentCloseReason.load()); + info.peer = SRT_CLOSE_REASON(m_PeerCloseReason.load()); + info.time = m_CloseTimeStamp.load().time_since_epoch().count(); +} diff --git a/srtcore/core.h b/srtcore/core.h index 02881cf9e..0c59bc62a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -214,7 +214,7 @@ class CUDT #if ENABLE_BONDING static SRTSOCKET connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); #endif - static SRTSTATUS close(SRTSOCKET u); + static SRTSTATUS close(SRTSOCKET u, int reason); static SRTSTATUS getpeername(SRTSOCKET u, sockaddr* name, int* namelen); static SRTSTATUS getsockname(SRTSOCKET u, sockaddr* name, int* namelen); static SRTSTATUS getsockdevname(SRTSOCKET u, char* name, size_t* namelen); @@ -470,7 +470,11 @@ class CUDT 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; } + void breakAsUnstable() + { + m_bBreakAsUnstable = true; + setAgentCloseReason(SRT_CLS_UNSTABLE); + } void ConnectSignal(ETransmissionEvent tev, EventSlot sl); void DisconnectSignal(ETransmissionEvent tev); @@ -613,10 +617,13 @@ class CUDT /// Close the opened UDT entity. - bool closeInternal() ATR_NOEXCEPT; + bool closeInternal(int reason) ATR_NOEXCEPT; void updateBrokenConnection(); void completeBrokenConnectionDependencies(int errorcode); + void setAgentCloseReason(int reason); + void setPeerCloseReason(int reason); + /// Request UDT to send out a data block "data" with size of "len". /// @param data [in] The address of the application data to be sent. /// @param len [in] The size of the data block. @@ -843,6 +850,15 @@ class CUDT 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; + + // If the socket was closed by some reason locally, the reason is + // in m_AgentCloseReason and the m_PeerCloseReason is then SRT_CLS_UNKNOWN. + // If the socket was closed due to reception of UMSG_SHUTDOWN, the reason + // exctracted from the message is written to m_PeerCloseReason and the + // m_AgentCloseReason == SRT_CLS_PEER. + sync::atomic m_AgentCloseReason; + sync::atomic m_PeerCloseReason; + atomic_time_point m_CloseTimeStamp; // Time when the close reason was first set bool m_bOpened; // If the UDT entity has been opened // 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. @@ -1111,7 +1127,7 @@ class CUDT void processCtrlDropReq(const CPacket& ctrlpkt); /// @brief Process incoming shutdown control packet - void processCtrlShutdown(); + void processCtrlShutdown(const CPacket& ctrlpkt); /// @brief Process incoming user defined control packet /// @param ctrlpkt incoming user defined packet void processCtrlUserDefined(const CPacket& ctrlpkt); @@ -1215,6 +1231,8 @@ class CUDT static const int SEND_LITE_ACK = sizeof(int32_t); // special size for ack containing only ack seq static const int PACKETPAIR_MASK = 0xF; + void copyCloseInfo(SRT_CLOSE_INFO&); + private: // Timers functions #if ENABLE_BONDING time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle diff --git a/srtcore/group.cpp b/srtcore/group.cpp index ed4708cef..f6a696349 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1112,7 +1112,7 @@ void CUDTGroup::close() { try { - CUDT::uglobal().close(*i); + CUDT::uglobal().close(*i, SRT_CLS_INTERNAL); } catch (CUDTException&) { @@ -2471,6 +2471,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) LOGC(grlog.Error, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + ps->core().setAgentCloseReason(SRT_CLS_ROGUE); ps->core().m_bBroken = true; broken.insert(ps); continue; @@ -3583,7 +3584,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " while waiting for any writable socket - CLOSING"); - CUDT::uglobal().close(s); // << LOCKS m_GlobControlLock, then GroupLock! + CUDT::uglobal().close(s, SRT_CLS_INTERNAL); // << LOCKS m_GlobControlLock, then GroupLock! } else { diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 180623039..4d3cc4b33 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -387,7 +387,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, case UMSG_SHUTDOWN: // 0101 - Shutdown // control info field should be none // but "writev" does not allow this - m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4); + m_PacketVector[PV_DATA].set(rparam, size); break; diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 224bc2794..89f501ac3 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -990,7 +990,8 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst LinkStatusInfo fi = *i; fi.errorcode = SRT_ECONNREJ; toRemove.push_back(fi); - i->u->sendCtrl(UMSG_SHUTDOWN); + uint32_t res[1] = {SRT_CLS_DEADLSN}; + i->u->sendCtrl(UMSG_SHUTDOWN, NULL, res, sizeof res); } } diff --git a/srtcore/srt.h b/srtcore/srt.h index b37a94b28..7eb161f5b 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -599,6 +599,37 @@ enum SRT_REJECT_REASON #define SRT_REJC_USERDEFINED 2000 // User defined error codes +enum SRT_CLOSE_REASON +{ + SRT_CLS_UNKNOWN, // Unset + SRT_CLS_INTERNAL, // Closed by internal reasons during connection attempt + SRT_CLS_PEER, // Received SHUTDOWN message from the peer + SRT_CLS_RESOURCE, // Problem with resource allocation + SRT_CLS_ROGUE, // Received wrong data in the packet + SRT_CLS_OVERFLOW, // Emergency close due to receiver buffer overflow + SRT_CLS_IPE, // Internal program error + SRT_CLS_API, // The application called srt_close() + SRT_CLS_FALLBACK, // Used for peer that do not support close reason feature + SRT_CLS_LATE, // Accepted-socket late-rejection or in-handshake rollback + SRT_CLS_CLEANUP, // All sockets are being closed due to srt_cleanup() call + SRT_CLS_DEADLSN, // This is an accepted socket off a dead listener + SRT_CLS_PEERIDLE, // Peer didn't send any packet for a time of SRTO_PEERIDLETIMEO + SRT_CLS_UNSTABLE, // Requested to be broken as unstable in Backup group + + SRT_CLS_E_SIZE +}; + +typedef struct SRT_CLOSE_INFO +{ + enum SRT_CLOSE_REASON agent; + enum SRT_CLOSE_REASON peer; + int64_t time; +} SRT_CLOSE_INFO; + +#define SRT_CLSC_INTERNAL 0 +#define SRT_CLSC_USER 100 + + // Logging API - specialization for SRT. // WARNING: This part is generated. @@ -822,6 +853,8 @@ SRT_API SRTSTATUS srt_rendezvous (SRTSOCKET u, const struct sockaddr* lo const struct sockaddr* remote_name, int remote_namelen); SRT_API SRTSTATUS srt_close (SRTSOCKET u); +SRT_API SRTSTATUS srt_close_withreason(SRTSOCKET u, int reason); +SRT_API SRTSTATUS srt_close_getreason(SRTSOCKET u, SRT_CLOSE_INFO* info); SRT_API SRTSTATUS srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); SRT_API SRTSTATUS srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); SRT_API SRTSTATUS srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 0e5f15c4b..3b3b35c34 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -24,6 +24,7 @@ written by #include "common.h" #include "packet.h" #include "core.h" +#include "api.h" #include "utilities.h" using namespace std; @@ -170,7 +171,33 @@ SRTSTATUS srt_close(SRTSOCKET u) return SRT_STATUS_OK; } - return CUDT::close(u); + return CUDT::close(u, SRT_CLS_API); +} + +SRTSTATUS srt_close_withreason(SRTSOCKET u, int reason) +{ + SRT_SOCKSTATUS st = srt_getsockstate(u); + + if ((st == SRTS_NONEXIST) || + (st == SRTS_CLOSED) || + (st == SRTS_CLOSING) ) + { + // It's closed already. Do nothing. + return SRT_STATUS_OK; + } + + if (reason < SRT_CLSC_USER) + reason = SRT_CLS_API; + + return CUDT::close(u, reason); +} + +SRTSTATUS srt_close_getreason(SRTSOCKET u, SRT_CLOSE_INFO* info) +{ + if (!info || u == SRT_INVALID_SOCK) + return SRT_ERROR; + + return CUDT::uglobal().getCloseReason(u, *info); } SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b2b9c332c..7b7a9a9b3 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -59,6 +59,50 @@ bool transmit_use_sourcetime = false; int transmit_retry_connect = 0; bool transmit_retry_always = false; +struct CloseReasonMap +{ + map at; + + CloseReasonMap() + { + at[SRT_CLS_UNKNOWN] = "Unset"; + at[SRT_CLS_INTERNAL] = "Closed by internal reasons during connection attempt"; + at[SRT_CLS_PEER] = "Received SHUTDOWN message from the peer"; + at[SRT_CLS_RESOURCE] = "Problem with resource allocation"; + at[SRT_CLS_ROGUE] = "Received wrong data in the packet"; + at[SRT_CLS_OVERFLOW] = "Emergency close due to receiver buffer overflow"; + at[SRT_CLS_IPE] = "Internal program error"; + at[SRT_CLS_API] = "The application called srt_close()"; + at[SRT_CLS_FALLBACK] = "The peer doesn't support close reason feature"; + at[SRT_CLS_LATE] = "Accepted-socket late-rejection or in-handshake rollback"; + at[SRT_CLS_CLEANUP] = "All sockets are being closed due to srt_cleanup() call"; + at[SRT_CLS_DEADLSN] = "This was an accepted socket off a dead listener"; + at[SRT_CLS_PEERIDLE] = "Peer didn't send any packet for a time of SRTO_PEERIDLETIMEO"; + at[SRT_CLS_UNSTABLE] = "Requested to be broken as unstable in Backup group"; + } + + string operator[](SRT_CLOSE_REASON reason) + { + if (int(reason) >= SRT_CLSC_USER) + { + string extra; + if (reason == SRT_CLSC_USER) + extra = " - Application exit due to interrupted transmission"; + + if (reason == SRT_CLSC_USER + 1) + extra = " - Error during configuration, transmission not started"; + + return Sprint("User-defined reason #", reason - SRT_CLSC_USER, extra); + } + + auto p = at.find(reason); + if (p == at.end()) + return "UNDEFINED"; + return p->second; + } + +} g_close_reason; + // Do not unblock. Copy this to an app that uses applog and set appropriate name. //srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-test"); @@ -765,7 +809,7 @@ void SrtCommon::Init(string host, int port, string path, map par, if (m_bindsock != SRT_INVALID_SOCK) srt_close(m_bindsock); if (m_sock != SRT_INVALID_SOCK) - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); m_sock = m_bindsock = SRT_INVALID_SOCK; throw; } @@ -1389,7 +1433,7 @@ void SrtCommon::ConnectClient(string host, int port) continue; } - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); Error("srt_connect", reason); } break; @@ -1480,6 +1524,9 @@ void SrtCommon::ConnectClient(string host, int port) void SrtCommon::Error(string src, int reason, int force_result) { + SRT_CLOSE_INFO cli; + SRTSTATUS cls = srt_close_getreason(m_sock, &cli); + int errnov = 0; const int result = force_result == 0 ? srt_getlasterror(&errnov) : force_result; if (result == SRT_SUCCESS) @@ -1502,9 +1549,23 @@ void SrtCommon::Error(string src, int reason, int force_result) else { if ( Verbose::on ) - Verb() << "FAILURE\n" << src << ": [" << result << "." << errnov << "] " << message; + Verb() << "FAILURE\n" << src << ": [" << result << "." << errnov << "] " << message; else - cerr << "\nERROR #" << result << "." << errnov << ": " << message << endl; + cerr << "\nERROR #" << result << "." << errnov << ": " << message << endl; + } + + if (cls == SRT_STATUS_OK) + { + int64_t srt_now = srt_time_now(); + int64_t ago = srt_now - cli.time; + cerr << "CLOSE REASON: ->\n\tagent=" << cli.agent << " [" << g_close_reason[cli.agent] + << "]\n\tpeer=" << cli.peer << " [" << g_close_reason[cli.peer] + << "]\n\ttime=" << cli.time << " (" << fixed << (ago/1000000.0) << "s ago)" + << endl; + } + else + { + cerr << "(CLOSE REASON not found)\n"; } throw TransmissionError("error: " + src + ": " + message); @@ -1534,7 +1595,7 @@ void SrtCommon::SetupRendezvous(string adapter, string host, int port) SRTSTATUS stat = srt_bind(m_sock, localsa.get(), localsa.size()); if (stat == SRT_ERROR) { - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); Error("srt_bind"); } } @@ -1551,8 +1612,21 @@ void SrtCommon::Close() { Verb() << "SrtCommon: DESTROYING CONNECTION, closing socket (rt%" << m_sock << ")..."; srt_setsockflag(m_sock, SRTO_SNDSYN, &yes, sizeof yes); - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER); any = true; + + SRT_CLOSE_INFO cli; + SRTSTATUS cls = srt_close_getreason(m_sock, &cli); + + if (cls == SRT_STATUS_OK) + { + int64_t srt_now = srt_time_now(); + int64_t ago = srt_now - cli.time; + cerr << "POST-FACTUM CLOSE REASON: ->\n\tagent=" << cli.agent << " [" << g_close_reason[cli.agent] + << "]\n\tpeer=" << cli.peer << " [" << g_close_reason[cli.peer] + << "]\n\ttime=" << cli.time << " (" << fixed << (ago/1000000.0) << "s ago)" + << endl; + } } if (m_bindsock != SRT_INVALID_SOCK) @@ -1560,7 +1634,7 @@ void SrtCommon::Close() Verb() << "SrtCommon: DESTROYING SERVER, closing socket (ls%" << m_bindsock << ")..."; // Set sndsynchro to the socket to synch-close it. srt_setsockflag(m_bindsock, SRTO_SNDSYN, &yes, sizeof yes); - srt_close(m_bindsock); + srt_close_withreason(m_bindsock, SRT_CLSC_USER); any = true; } @@ -1835,8 +1909,8 @@ SRTSTATUS SrtTarget::ConfigurePre(SRTSOCKET sock) if (result == SRT_ERROR) return result; - if (sock & SRTGROUP_MASK) - return 0; + if (int(sock) & SRTGROUP_MASK) + return SRT_STATUS_OK; int yes = 1; // This is for the HSv4 compatibility; if both parties are HSv5 From 44d4e7975b148e6f043a074e315d9409b32d1147 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 21 Mar 2025 11:44:20 +0100 Subject: [PATCH 168/174] Added fixes to sync with dev --- docs/API/API-functions.md | 3 --- srtcore/core.cpp | 1 - srtcore/epoll.cpp | 6 ------ 3 files changed, 10 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 161f7a312..ff2090232 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1480,7 +1480,6 @@ of group members | | | - | in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | |:---------:|:--------------:|:---------------:|:----------:|:------------:|:---------------------------------:| | ptr | ≥ group.size | `SRT_STATUS_OK` | group.data | group.size() | ✖️ | @@ -1545,7 +1544,6 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f * [`errorcode`](#error-codes): status of connection for that link ([`SRT_SUCCESS`](#srt_success) if succeeded) * `token`: same as in input, or a newly created token value if input was -1 - | Returns | | |:----------------------------- |:-------------------------------------------------- | | Socket ID | The socket ID of the first connected member. | @@ -3705,7 +3703,6 @@ including the value of `SRTO_IPV6ONLY` flag), must be exactly identical. If any of these conditions isn't satisfied, the `srt_bind` function results in conflict and report this error. - #### SRT_EASYNCFAIL General asynchronous failure (not in use currently). diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cef880b7f..d1bd8c4ad 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3687,7 +3687,6 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) CNetworkInterface use_source_adr; while (!m_bClosing && !m_bBroken) - //while (!m_bClosing && !m_bBroken) { const steady_clock::time_point local_tnow = steady_clock::now(); const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load(); diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 47c883ec3..6f03f5adc 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -880,12 +880,6 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return int(SRT_ERROR); } - if (uid == SRT_INVALID_SOCK || uid == 0) - { - LOGC(eilog.Fatal, log << "epoll/update: IPE: invalid 'uid' submitted for update!"); - return -1; - } - int nupdated = 0; vector lost; From 68e7442a1d28273d1fa043eecc84cbb4d21ce84a Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 21 Mar 2025 11:52:58 +0100 Subject: [PATCH 169/174] [HOTFIX] Formalize initialization of CloseInfo structure, per Sonar findings --- srtcore/api.cpp | 9 ++++++--- srtcore/api.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4023cf9a4..46793bc5f 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2210,10 +2210,13 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) // [[using locked(m_GlobControlLock)]] void srt::CUDTUnited::recordCloseReason(CUDTSocket* s) { + SRT_CLOSE_INFO info; + info.agent = SRT_CLOSE_REASON(s->core().m_AgentCloseReason.load()); + info.peer = SRT_CLOSE_REASON(s->core().m_PeerCloseReason.load()); + info.time = s->core().m_CloseTimeStamp.load().time_since_epoch().count(); + CloseInfo ci; - ci.info.agent = SRT_CLOSE_REASON(s->core().m_AgentCloseReason.load()); - ci.info.peer = SRT_CLOSE_REASON(s->core().m_PeerCloseReason.load()); - ci.info.time = s->core().m_CloseTimeStamp.load().time_since_epoch().count(); + ci.info = info; m_ClosedDatabase[s->m_SocketID] = ci; diff --git a/srtcore/api.h b/srtcore/api.h index 850af09a6..0665a5077 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -586,7 +586,7 @@ class CUDTUnited // to remove the record. As GC rolls every 1 second, // this is more-less the number of seconds this record // will be alive AFTER you close the socket. - CloseInfo(): generation(MAX_CLOSE_RECORD_TTL) {} + CloseInfo(): info(), generation(MAX_CLOSE_RECORD_TTL) {} }; std::map m_ClosedDatabase; From 3c159ed1f6a87501db35bcac7b1a0347b1bf0441 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 8 Apr 2025 16:40:03 +0200 Subject: [PATCH 170/174] [CI] Upgraded Ubuntu version for cxx11-ubuntu over deprecated 20.04 --- .github/workflows/cxx11-ubuntu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 78721a7e3..e705c24e4 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: ubuntu - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: BUILD_WRAPPER_OUT_DIR: sonar-output # Directory where build-wrapper output will be placed steps: From 2201f5696d9e915e32fde92ae538a9f7768fad68 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 8 Apr 2025 16:58:39 +0200 Subject: [PATCH 171/174] [REFAX] Refactored function calls at close socket for preparing a change (#2639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [REFAX] Refactored function calls at close socket for preparing a change --------- Co-authored-by: Mikołaj Małecki Co-authored-by: Maxim Sharabayko --- srtcore/api.cpp | 73 +++++++++++++++++++++++++++++++++++-------------- srtcore/api.h | 13 +++++++++ 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 46793bc5f..54d37ffbd 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -180,7 +180,6 @@ srt::CUDTUnited::CUDTUnited() , 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); @@ -301,7 +300,10 @@ void srt::CUDTUnited::closeAllSockets() s->removeFromGroup(false); } #endif - m_ClosedSockets[i->first] = s; + // NOTE: not removing the socket from m_Sockets. + // This is a loop over m_Sockets and after this loop ends, + // this whole container will be cleared. + swipeSocket_LOCKED(i->first, s, SWIPE_LATER); if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { @@ -534,6 +536,16 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, CUDTUnited::SwipeSocketTerm lateremove) +{ + m_ClosedSockets[id] = s; + if (!lateremove) + { + m_Sockets.erase(id); + } +} + int srt::CUDTUnited::newConnection(const SRTSOCKET listener, const sockaddr_any& peer, const CPacket& hspkt, @@ -882,8 +894,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listener, // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(id, ns->core().m_sPollID); - m_Sockets.erase(id); - m_ClosedSockets[id] = ns; + swipeSocket_LOCKED(id, ns, SWIPE_NOW); } return -1; @@ -2378,8 +2389,7 @@ SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s, int reason) // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(s->m_SocketID, s->core().m_sPollID); - m_Sockets.erase(s->m_SocketID); - m_ClosedSockets[s->m_SocketID] = s; + swipeSocket_LOCKED(s->m_SocketID, s, SWIPE_NOW); HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); CGlobEvent::triggerEvent(); @@ -3062,7 +3072,10 @@ void srt::CUDTUnited::checkBrokenSockets() // close broken connections and start removal timer s->setClosed(); tbc.push_back(i->first); - m_ClosedSockets[i->first] = s; + + // NOTE: removal from m_SocketID POSTPONED + // to loop over removal of all from the `tbc` list + swipeSocket_LOCKED(i->first, s, SWIPE_LATER); if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { @@ -3183,9 +3196,6 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) s->removeFromGroup(true); } #endif - // decrease multiplexer reference count, and remove it if necessary - const int mid = s->m_iMuxID; - { ScopedLock cg(s->m_AcceptLock); @@ -3211,8 +3221,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // You won't be updating any EIDs anymore. m_EPoll.wipe_usock(as->m_SocketID, as->core().m_sPollID); - m_ClosedSockets[q->first] = as; - m_Sockets.erase(q->first); + swipeSocket_LOCKED(q->first, as, SWIPE_NOW); } } @@ -3247,16 +3256,37 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // Report: P04-1.28, P04-2.27, P04-2.50, P04-2.55 HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); + leaveCS(m_GlobControlLock); s->core().closeInternal(SRT_CLS_INTERNAL); enterCS(m_GlobControlLock); + + // IMPORTANT!!! + // + // The order of deletion must be: first delete socket, then multiplexer. + // The receiver buffer shares the use of CUnits from the multiplexer's unit queue, + // which is assigned to the multiplexer because this is where the incoming + // UDP packets are placed. The receiver buffer must be first deleted and + // so unreference all CUnits. Then the multiplexer can be deleted and drag all + // CUnits with itself. + const int mid = s->m_iMuxID; HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; - HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer."); + HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer id=" << mid); + removeMux(mid); +} +/// decrease multiplexer reference count, and remove it if necessary +/// +/// @param mid Muxer ID that identifies the multiplexer in the socket +/// @param u Socket ID that was the last multiplexer's user (logging only) +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::removeMux(const int mid) +{ + // Ignore those never bound if (mid == -1) { - HLOGC(smlog.Debug, log << "GC/removeSocket: no muxer found, finishing."); + HLOGC(smlog.Debug, log << "MUXER not assigned to that socket"); return; } @@ -3264,19 +3294,18 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) m = m_mMultiplexer.find(mid); if (m == m_mMultiplexer.end()) { - LOGC(smlog.Fatal, log << "IPE: For socket @" << u << " MUXER id=" << mid << " NOT FOUND!"); + LOGC(smlog.Fatal, log << "IPE: MUXER id=" << mid << " NOT FOUND!"); return; } CMultiplexer& mx = m->second; mx.m_iRefCount--; - HLOGC(smlog.Debug, log << "unrefing underlying muxer " << mid << " for @" << u << ", ref=" << mx.m_iRefCount); - if (0 == mx.m_iRefCount) + HLOGC(smlog.Debug, log << "removeMux: unrefing muxer " << mid << ", ref=" << mx.m_iRefCount); + if (mx.m_iRefCount <= 0) { - HLOGC(smlog.Debug, - log << "MUXER id=" << mid << " lost last socket @" << u << " - deleting muxer bound to port " - << mx.m_pChannel->bindAddressAny().hport()); + HLOGC(smlog.Debug, log << "MUXER id=" << mid << " lost last socket - deleting muxer bound to " + << mx.m_pChannel->bindAddressAny().str()); // The channel has no access to the queues and // it looks like the multiplexer is the master of all of them. // The queues must be silenced before closing the channel @@ -3287,6 +3316,10 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) mx.destroy(); m_mMultiplexer.erase(m); } + else + { + HLOGC(smlog.Debug, log << "MUXER id=" << mid << " has still " << mx.m_iRefCount << " users"); + } } void srt::CUDTUnited::checkTemporaryDatabases() diff --git a/srtcore/api.h b/srtcore/api.h index 0665a5077..ab7f8107d 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -274,6 +274,18 @@ class CUDTUnited /// @return The new UDT socket ID, or INVALID_SOCK. SRTSOCKET newSocket(CUDTSocket** pps = NULL); + enum SwipeSocketTerm { SWIPE_NOW = 0, SWIPE_LATER = 1 }; + /// Removes the socket from the global socket container + /// and place it in the socket trashcan. The socket should + /// remain there until all still pending activities are + /// finished and there are no more users of this socket. + /// Note that the swiped socket is no longer dispatchable + /// by id. + /// @param id socket ID to swipe. + /// @param s pointer to the socket to swipe. + /// @param action only add to closed list or remove completely + void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, SwipeSocketTerm); + /// Create (listener-side) a new socket associated with the incoming connection request. /// @param [in] listen the listening socket ID. /// @param [in] peer peer address. @@ -527,6 +539,7 @@ class CUDTUnited void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); + void removeMux(const int mid); // Utility functions for updateMux void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); From 02b2cc49d810fa98981e1f74b8c7dc4b829afe08 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 8 Apr 2025 17:00:58 +0200 Subject: [PATCH 172/174] Downgraded Ubuntu in cxx11-ubuntu to 22.04 (gtest failure) --- .github/workflows/cxx11-ubuntu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index e705c24e4..944337a7f 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: ubuntu - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 env: BUILD_WRAPPER_OUT_DIR: sonar-output # Directory where build-wrapper output will be placed steps: From acb56b8bd3b82d2d8c470cc76743aa08a14ac6eb Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 8 Apr 2025 17:55:11 +0200 Subject: [PATCH 173/174] [CI] Uplifted GTest download version to 1.12 (1.10 was buggy) --- scripts/googletest-download.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/googletest-download.cmake b/scripts/googletest-download.cmake index 7469f4f7b..be0adb7d6 100644 --- a/scripts/googletest-download.cmake +++ b/scripts/googletest-download.cmake @@ -11,7 +11,7 @@ ExternalProject_Add( BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build" GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.10.0 + GIT_TAG release-1.12.0 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 64b6ac1b53dd9e5c54dbdb02785803c490fa3d4f Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 9 Apr 2025 09:00:17 +0200 Subject: [PATCH 174/174] [DEV] Updated ABI checker to use correct version ABI checks. (#3156) * [DEV/CI] Updated ABI checker to use correct version ABI checks. --------- Co-authored-by: Mikolaj Malecki --- .github/workflows/abi.yml | 78 +++++++++++++++++++++++++---------- CMakeLists.txt | 2 +- scripts/abi-check.tcl | 12 +++++- scripts/get-build-version.tcl | 47 +++++++++++++++++++++ 4 files changed, 114 insertions(+), 25 deletions(-) create mode 100755 scripts/get-build-version.tcl diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml index 4bd01bb59..a144ee407 100644 --- a/.github/workflows/abi.yml +++ b/.github/workflows/abi.yml @@ -10,52 +10,86 @@ env: SRT_BASE: v1.5.0 jobs: - build: - name: ABI checks + build_pr: + name: Build current version runs-on: ubuntu-20.04 + outputs: + SRT_BASE: ${{ steps.commands.outputs.SRT_BASE }} steps: - uses: actions/checkout@v3 with: - path: pull_request + path: gitview_pr - name: configure run: | - cd pull_request + cd gitview_pr mkdir _build && cd _build cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ - name: build run: | sudo apt install -y abi-dumper - cd pull_request/_build && cmake --build ./ + sudo apt install -y tcl + cd gitview_pr/_build && cmake --build ./ make install DESTDIR=./installdir - SRT_TAG_VERSION=$(cat version.h |grep SRT_VERSION_MINOR |head -n1 |awk {'print $3'}) - abi-dumper libsrt.so -o libsrt-pr.dump -public-headers installdir/usr/local/include/srt/ -lver 0 - SRT_BASE="v1.$SRT_TAG_VERSION.0" - echo "SRT_BASE=$SRT_BASE" >> "$GITHUB_ENV" + SRT_TAG_VERSION=v$(../scripts/get-build-version.tcl full) + SRT_TAG=${SRT_TAG_VERSION}dev-$(git rev-parse --short HEAD) + abi-dumper libsrt.so -o libsrt-pr.dump -public-headers installdir/usr/local/include/srt/ -lver $SRT_TAG + SRT_BASE=v$(../scripts/get-build-version.tcl base) + if [[ $SRT_TAG_VERSION == $SRT_BASE ]]; then + echo "NOT CHECKING ABI: base version is being built: $SRT_TAG" + echo "SRT_BASE=''" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "SRT_BASE=$SRT_BASE" >> "$GITHUB_OUTPUT" + + build_base: + name: Build base version + runs-on: ubuntu-20.04 + needs: build_pr + if: ${{ needs.build_pr.outputs.SRT_BASE != '' }} + + env: + SRT_BASE: ${{ needs.build_pr.outputs.SRT_BASE }} + steps: - uses: actions/checkout@v3 with: - path: tag + path: gitview_base ref: ${{ env.SRT_BASE }} - name: configure_tag run: | echo $SRT_TAG_VERSION - cd tag + cd gitview_base mkdir _build && cd _build cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ - name: build_tag run: | - cd tag + cd gitview_base cd _build && cmake --build ./ make install DESTDIR=./installdir - abi-dumper libsrt.so -o libsrt-tag.dump -public-headers installdir/usr/local/include/srt/ -lver 1 + abi-dumper libsrt.so -o libsrt-base.dump -public-headers installdir/usr/local/include/srt/ -lver $SRT_BASE + + check_abi: + name: Compare ABI + runs-on: ubuntu-20.04 + needs: [build_pr, build_base] + env: + SRT_BASE: ${{ needs.build_pr.outputs.SRT_BASE }} + + steps: - name: abi-check run: | - git clone https://github.com/lvc/abi-compliance-checker.git - cd abi-compliance-checker && sudo make install && cd ../ - abi-compliance-checker -l libsrt -old tag/_build/libsrt-tag.dump -new pull_request/_build/libsrt-pr.dump - RES=$? - if (( $RES != 0 )) - then - echo "ABI/API Compatibility check failed with value $?" - exit $RES - fi + #git clone https://github.com/lvc/abi-compliance-checker.git + cd gitview_pr/submodules + git submodule update --init abi-compliance-checker + cd abi-compliance-checker && sudo make install && cd ../ + cd ../.. + abi-compliance-checker -l libsrt -old gitview_base/_build/libsrt-base.dump -new gitview_pr/_build/libsrt-pr.dump + RES=$? + if (( $RES != 0 )); then + echo "ABI/API Compatibility check failed with value $?" + exit $RES + fi + - name: Download report + uses: actions/download-artifact@v4 + with: + path: compat_reports diff --git a/CMakeLists.txt b/CMakeLists.txt index 38ed9e716..ca09d1fb6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.5.4) +set (SRT_VERSION 1.6.0) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables diff --git a/scripts/abi-check.tcl b/scripts/abi-check.tcl index 15a585de3..7f9c70e79 100755 --- a/scripts/abi-check.tcl +++ b/scripts/abi-check.tcl @@ -56,8 +56,16 @@ if {![file isdirectory $olddir] || ![file isdirectory $newdir]} { exit 1 } -generate-abi-dump $olddir 1 -generate-abi-dump $newdir 0 +set wd [pwd] +cd $olddir +set base_ver [exec $top/scripts/get-build-version.tcl] +cd $wd +cd $newdir +set new_ver [exec $top/scripts/get-build-version.tcl] +cd $wd + +generate-abi-dump $olddir $base_ver +generate-abi-dump $newdir $new_ver set res [catch {exec >@stdout 2>@stderr $abichecker -l libsrt -old $olddir/libsrt-abi.dump -new $newdir/libsrt-abi.dump} out] diff --git a/scripts/get-build-version.tcl b/scripts/get-build-version.tcl new file mode 100755 index 000000000..fafda061a --- /dev/null +++ b/scripts/get-build-version.tcl @@ -0,0 +1,47 @@ +#!/usr/bin/tclsh + +if {![file exists version.h]} { + puts stderr "No version.h file found - run this in the build directory!" + exit 1 +} + +set fd [open version.h r] + +set version "" +set line "" +while {[gets $fd line] != -1} { + # Use regexp because this is safer to avoid + # unexpected list interpretation mistakes + if {[regexp {#define SRT_VERSION_STRING} $line]} { + # Once matched this way, we can safely use tcl-list-like access + set version [lindex $line 2 0] + break + } +} +close $fd + +if {$version == ""} { + puts stderr "No version extracted (no SRT_VERSION_STRING found)" + exit 1 +} + +lassign $argv model part + +if {$model == ""} { + set model current +} + +lassign [split $version .] major minor patch + +if {$part == "minor"} { + set prefix "$minor" +} else { + set prefix "$major.$minor" +} + +if {$model == "base"} { + puts "$prefix.0" +} else { + puts "$prefix.$patch" +} +